CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

时间:2022-10-17 16:54:32

近期Shiro修复了一个漏洞,1.10.0之前的版本在请求forward时不进行拦截鉴权

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 

如下测试代码,方法1不需要权限,方法2配置了authc,方法1转发方法2,则可以绕过方法2的鉴权

Controller

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 ShiroConfig

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 

目录

ShiroFilter在两个版本间的代码区别

1.7版本

1.10版本

SpringBoot对与外部Filter的集成过程

FilterRegistrationBean执行流程

总结


ShiroFilter在两个版本间的代码区别

1.7在forward请求时会跳过,1.10默认情况下forward请求也会走过滤

1.7版本

org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter shiro-web.jar

在该请求处理过第一次后,为请求添加了属性shiroFilter.FILTERED=true, 在第二次forward请求进来时会进到第一个if跳过本Filter的执行

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 

1.10版本

org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter shiro-web.jar

增加了一个属性filterOncePerRequest来判断是否需要每个请求都进行过滤,如果为true则不过滤forward请求,如果为false每个请求都会走一遍鉴权

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 filterOncePerRequest属性在1.10之后新增,默认是false,也就是过滤forward

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 

虽然shiro本身支持了forward过滤,但是在springBoot下使用shiro1.10新特性,不只是改个版本号

SpringBoot对与外部Filter的集成过程

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh

--》org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer

--》org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getSelfInitializer

--》org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize

在这里获取各种初始化类,调用onStartup方法进行web服务器的初始化

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 getServletContextInitializerBeans方法返回是一个集合类,查找容器中所有的Filter,Servlet,ServletContextInitializer等等相关的初始化bean

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 addServletContextInitializerBeans(beanFactory);

查询容器中所有ServletContextInitializer实现类放入this.initializer集合中,实现类列表如下, 可以看到Filter相关的实现类是FilterRegistrationBean,先忽略看圈内的第二个方法

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 addAdaptableBeans(beanFactory);

获取容器中Servlet/Filter类型的bean,将其作为RegistrationBean放入this.initializer集合,对于Filter来说,就是查询容器所有Filter类型的bean,封装为FilterRegistrationBean放入this.initializer集合

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 

所以由上边两拨代码可知,可以有以下几种方式引入ShiroFilter(应该还有其他方式@WebFilter/DelegatingFilterProxy等 ,这里不涉及,咱忽略)

1、ShiroFilterFactoryBean

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 如上配置之后,ShiroFilter作为bean被加载到spring容器,再由addAdaptableBeans(beanFactory);方法将其封装为FilterRegistrationBean,调用onStartup方法加载到Tomcat中

2、FilterRegistrationBean

直接向spring容器注入FilterRegistrationBean

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 

FilterRegistrationBean执行流程

顶层父类继承自ServletContextInitializer,自然从onStartup方法看起

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 先将filter放入servlet容器,再进行配置

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 将filter加入Tomcat的servletContext

配置Filter

我们重点看下边代码开头的DispatcherType配置,如果我们没有自己在FilterRegistrationBean配置的话,默认情况下ShiroFilter是只会处理REQUEST类型的请求,因为ShiroFilter继承的不是org.springframework.web.filter.OncePerRequestFilter

	@Override
	protected void configure(FilterRegistration.Dynamic registration) {
		super.configure(registration);
        // 这里DispatcherType表示请求类型
        // 首先设置Filter可以处理的请求类型
		EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
        // 如果没有自己设置DispatcherType
		if (dispatcherTypes == null) {
			T filter = getFilter();
            // 如果filter类型是org.springframework.web.filter.OncePerRequestFilter,则可以处理全部请求类型
			if (ClassUtils.isPresent("org.springframework.web.filter.OncePerRequestFilter",
					filter.getClass().getClassLoader()) && filter instanceof OncePerRequestFilter) {
				dispatcherTypes = EnumSet.allOf(DispatcherType.class);
			}
			else {
                // 否则只能处理REQUEST类型的请求
				dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
			}
		}
		Set<String> servletNames = new LinkedHashSet<>();
		for (ServletRegistrationBean<?> servletRegistrationBean : this.servletRegistrationBeans) {
			servletNames.add(servletRegistrationBean.getServletName());
		}
		servletNames.addAll(this.servletNames);
		if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
			registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter, DEFAULT_URL_MAPPINGS);
		}
		else {
			if (!servletNames.isEmpty()) {
				registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
						StringUtils.toStringArray(servletNames));
			}
			if (!this.urlPatterns.isEmpty()) {
				registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
						StringUtils.toStringArray(this.urlPatterns));
			}
		}
	}

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 注:ShiroFilter继承的也叫OncePerRequestFilter,同名,一个是spring的,一个是shiro的

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 

在文章头的测试用例中forward转发开始后会进入如下方法,将当前请求路径改成转发路径和请求类型改为FORWARD

org.apache.catalina.core.ApplicationDispatcher#doForward

最终进入如下方法重新进行Filter过滤和Servlet处理

org.apache.catalina.core.ApplicationDispatcher#invoke

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 在内部创建Filter链时,会先查询当前请求的Dispatcher_type,当前forward请求为FORWARD,然后在filter中查询能处理该请求的Filter组成链

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理

 

总结

SpringBoot集成ShiroFilter时,默认情况下ShiroFilter不拦截Forward或者Include请求,如果在SpringBoot下使用要对Forward请求拦截鉴权,不仅需要将shiro-spring版本升级到1.10.0,而且需要手动配置FilterRegistrationBean的DispatcherType

CVE-2022-40664 ShiroFilter1.7和1.10对于forward请求的处理