越权漏洞是日常开发中比较常见的一个缺陷
。要进行越权检测
,施垂一般需要明确定义和管理系统中的常测权限
。这可能包括用户角色、权措权检资源和操作的施垂细粒度权限控制。维护这些权限定义并确保它们与实际业务操作一致本身就是常测是一项复杂的任务。 以垂直越权为例,权措权检一般测试时,云计算施垂首先会获取到高权限用户模块的常测业务数据包,然后在BurpSuite将其鉴权凭证(一般是权措权检cookie)替换成低权限用户的 ,也可以构造好高权限的施垂业务数据包
,然后在浏览器登陆低权限用户
,常测通过访问相关的权措权检数据包
,若业务依旧可以成功访问
,施垂则存在垂直越权缺陷。但是实际测试过程中可能会遇到一系列的问题 : 除此以外 ,还有还多其他的因素,下面先看看SpringWeb中常见鉴权措施与解析顺序
,简单探索下能不能在一定程度上解决上述的问题
。服务器租用 过滤器是位于请求处理链的最外层,可以拦截请求并进行对应的处理。如果某资源已经配置对应filter进行处理的话,那么每次访问这个资源都会执行doFilter()方法,该方法也是过滤器的核心方法
。例如可以在调用目标资源之前
,进行权限等的处理。 过滤器是免费模板Servlet的实现规范 ,仅在tomcat等Web容器中调用 。Spring Boot默认内嵌Tomcat作为Web服务器。以tomcat-embed-core-9.0.64为例,查看Filter的具体调用过程
。 Filter调用时会在org.apache.catalina.cor.StandardWrapperValve#invoke()方法中被创建执行。主要是通过ApplicationFilterFactory.createFilterChain创建FilterChain: 查看createFilterChain方法的具体实现: 首先会检查 servlet是否为null,如果是表示没有指定Servlet
,就没有需要创建的过滤器链。模板下载否则根据不同的情况创建一个 ApplicationFilterChain对象或获取已存在的过滤器链对象。过滤器链对象负责管理一系列的过滤器: 然后获取所有的filter的映射对象,在filterMaps中保存的是各个filter的元数据信息,若filterMaps不为null且length不为0,会对前面创建的filterChain进一步的封装,首先会获取与当前请求相关的标识信息,例如请求的调度类型(dispatcher)和请求的路径(requestPath): 然后遍历所有过滤器映射,根据一定的条件判断将匹配的过滤器添加到过滤器链中。条件包括与调度类型的匹配和与请求路径或Servlet名称的匹配
: 最后,返回创建的过滤器链,该过滤器链包含了所有匹配的过滤器。如果没有找到匹配的过滤器
,则返回一个空的过滤器链。创建了filterChain之后,就开始执行ApplicationFilterChain的doFilter进行请求的链式处理 : 具体的逻辑在org.apache.catalina.core.ApplicationFilterChain#internalDoFilter方法 ,首先通过pos索引判断是否执行完了所有的filter,如果没有,取出当前待执行的索引filter,调用其doFilter方法: 当所有的filter执行完后 ,会释放掉过滤器链及其相关资源 。开始执行servlet业务模块servlet.service(request, response); 以上是tomcat中整个Filter的调用过程。而 Controller 中收到的请求 ,都是经过 Tomcat 容器解析后交给 DispatcherServlet,再由其转交给对应 Controller 的。 所以,过滤器是在Servlet容器级别处理请求的,因此会在Spring框架内部的其他组件之前执行 。 看一个实际的例子,验证前面的结论
: 这里通过FilterRegistrationBean实例进行注册
,将自定义的 AuthFilter 声明成 Bean 交给 Spring 管理
,在AuthFilter中对/admin/**目录下的资源进行了权限控制
: 假设/admin存在如下更新用户的接口: 该接口使用POST进行请求
,通过@RequestBody的方式进行数据的传输。未经过任何类似鉴权filter方式处理的话,若通过如下方式进行请求会返回相应的状态码: 因为过滤器会在Spring框架内部的其他组件之前执行,所以当AuthFilter逻辑生效时 ,前面的请求方式均会返回403 Status ,这里以GET请求为例: 拦截器是Spring框架提供的机制,用于在请求处理的不同阶段(如处理器方法前后、视图渲染前后)执行自定义逻辑。可以通过创建自定义拦截器,进行检查请求、修改模型数据或执行其他操作。 实现拦截器需要实现HandlerInterceptor这个接口,这个接口中有三个默认方法,这三个方法的执行顺序: 在鉴权场景中,拦截器常常用于在请求处理前执行,也就是一般会调用preHandle
,后续仅讨论preHandle的情况。 过滤器是在DispatcherServlet处理之前拦截,而拦截器是在DispatcherServlet处理请求然后调用Controller方法之前进行拦截
。所以具体查看DispatcherServlet是怎么对拦截器进行处理的: 当Spring MVC接收到请求时 ,Servlet容器会调用DispatcherServlet的service方法。这里会调用doDispatch方法进行进一步的处理
。来获取对应的mappedHandler: 在getHandler方法中
,会顺序循环调用HandlerMapping的getHandler方法: 首先会通过RequestMappingHandlerMapping处理,在其getHandler方法中通过getHandlerInternal获取handler构建HandlerExecutionChain并返回 ,这里会添加该请求相关的所有Interceptor: 在getHandlerExecutionChain方法中,首先会创建一个HandlerExecutionChain对象,用于存储处理器和拦截器 。这里会遍历 adaptedInterceptors的拦截器集合,如果拦截器是 MappedInterceptor的实例,并且它的 matches(request)方法返回 true(表示请求的URL路径匹配该拦截器)
,则将该拦截器中的实际拦截器添加到 chain中。否则直接将它添加到 chain中
,无需进行路径匹配 : 最后会返回构建好的 HandlerExecutionChain对象 chain,其中包含了处理程序和相应的拦截器,以便在处理HTTP请求时按照一定的顺序执行这些拦截器操作
。处理完后会获取处理器适配器,然后调用applyPreHandle方法进行处理
: 这里实际就是执行拦截器前置处理preHandle方法: 后续会执行具体Controller下的服务,以及执行HandlerInterceptor的PostHandle和AfterCompletion方法,这里也映证了preHandle会在Controller方法执行之前执行: 以上是拦截器Interceptor的大致执行流程。 看一个实际的例子,验证前面的结论: 通过实现HandlerInterceptor 接口自定义拦截器AuthInterceptor: 在配置类添加@Configuration注解 ,通过重写addInterceptors方法 ,添加拦截器,并配置匹配路径 ,AuthInterceptor对/admin/目录下的所有资源都生效,也就是说这是一个垂直鉴权的措施
: 同样是前面过滤器Filter中访问/user/add接口的例子
,当拦截器AuthInterceptor生效时,再次使用对应的方式请求会返回403 Status
,说明不存在对应的身份无法访问: 使用拦截器preHandle进行鉴权时 ,对应的解析逻辑会在Controller方法执行之前执行 。与Controller的参数、Content-Type发送的数据类型无关
。 主要是检查请求是否具备足够的权限访问某个资源,而不是检查请求的具体内容
。 但是要注意一点,跟过滤器Filter不同的是,类似上面的案例,当拦截器AuthInterceptor生效时,使用GET方式请求/admin/user/add(POST请求接口)接口并不会返回403 Stauts
: 这是因为在RequestMappingHandlerMapping的getHandler方法中通过getHandlerInternal获取handler时 ,会对请求匹配对应的handler ,其中一处就是请求Method的匹配
,若资源不存在的话
,也没必要进一步处理了
: 切面是Spring的AOP(面向切面编程)组件,用于定义横切关注点。虽然它们通常用于横切关注点的日志记录
、性能监控等
,但也可以用于鉴权。你可以创建自定义切面
,它们可以拦截方法调用,以执行鉴权逻辑。 相比其他措施, 切面的执行顺序十分灵活 ,可以通过配置进行管理,它们可以被指定在其他组件之前或之后执行
。 最常见的是通过注解方式来实现,主要包含以下解释: 例如如下的案例
,代码中只要被@Auth注解标记的方法均会执行当前切面定义的内容: @Before
:在目标方法执行之前
,执行注解标记的内容 @After :在目标方法执行之后,执行注解标记的内容 @AfterReturning:在目标方法返回后 ,执行注解标记的内容 @AfterThrowing:在目标方法抛出异常后
,执行注解标记的内容 @Around:在目标方法执行前后 ,分别执行对应的内容 在 权限校验中 ,比较常用的是@Before和@Around
。例如@Around,在判断权限之后选择对应的函数是否执行
。如果权限满足
,那么执行函数,如果不满足直接抛出权限不足的提示。 相比Filter和Interceptor, 切面在方法级别执行 ,并且可以拿到Controller方法的参数进行操作
。 除了上面的方式以外
,如果对应鉴权规则是基于业务逻辑的,一般还会在 Service 层实现鉴权 。 Service 层通常包含业务逻辑和数据访问 ,可以更好地控制鉴权逻辑
。与路径级别的鉴权不同 ,在 Service 层实施鉴权可以确保所有相关的业务方法都经过相同的鉴权规则。 例如下面的例子,UserService中包含了一个hasPermissionToDeleteUser方法
,该方法根据自定义的鉴权逻辑来检查是否允许当前用户删除指定的用户 。然后,在deleteUser方法中,我们检查是否有权限删除用户,并根据鉴权结果来执行删除用户的业务逻辑或抛出异常: 因为Service的调用一般都是在Controller,此时已经完成路径解析&匹配了,相比于前面的措施
,基本上是最后才进行解析。 根据上面的分析,可以大概知道
,当一个请求到达时
,执行顺序是:Filter过滤器> Interceptor拦截器> ControllerAdvice > AOP > Controller,在Controller之后,就是具体的service调用了
。 对于垂直越权的场景,一般情况下在系统设计开发时
,会根据路由进行权限角色的区分,过拦截器(Interceptor)或过滤器(Filter)之类的中间件组件来保护应用程序。 以SpringSecurity为例 ,Spring Security内部其实是通过一个过滤器链来实现认证/鉴权等流程的。例如下面的例子
,这里限制了/admin/以及/manage目录下的接口均需要ADMIN角色才能进行访问: 根据前面的分析 ,也就是说不论/admin下的接口参数是否已知
,是否有完整的请求流量,都可以通过直接请求接口的方式进行验证,若返回接口不是403,则可能存在垂直越权的风险
: 对于基于Spring开发系统,可以结合鉴权措施的执行顺序,在一定程度上解决垂直越权测试上的问题 ,但是对于平行越权来说,一般情况下的防护措施是基于业务逻辑的,一般会在 Service 层实现鉴权 。这里就必须获取到对应的请求参数了或者完整的流量了。 本文作者:SecIN技术社区
, 转载请注明来自FreeBuf.COM一、常测越权测试中的权措权检痛点/难点
二 、SpringWeb中常见鉴权措施
2.1 过滤器Filter
wKg0C2UZa3mAcUOHAADOEBt3mHs966.png
wKg0C2UZa7aAAQAABBWzHMHw483.png
wKg0C2UZa8AfjG8AACN0FNuSC8022.png
wKg0C2UZayAMT7gAAC5eGoKgU526.png
wKg0C2UZbAaAJnU6AAArmzqLg216.png
wKg0C2UZbCCADH5nAACSN2AJnsU546.png
wKg0C2UZbD6AYDHSAABgcy22c8293.png
wKg0C2UZbIWAK8kdAACg0gYo8ec207.png
wKg0C2UZbJAIXY5AACwhbZnFbo493.png
wKg0C2UZbLKAaIkPAACm5tOsjQ861.png
wKg0C2UZbMOAVoO7AACQPalukXo530.png2.2 拦截器Interceptor( preHandle)
wKg0C2UZbXSAa7R1AABPnZI54iE784.png
wKg0C2UZbZOAcQcAABcsXe4svI564.png
wKg0C2UZbaAWY7EAACWNQfmjc257.png
wKg0C2UZbdKAJZRxAACIcsItVQ657.png
wKg0C2UZbemABcz3AAAroZqycWc460.png
wKg0C2UZbgSAZ9rkAABr9GOWJU163.png
wKg0C2UZbhuAWdmhAABeSb0zcas924.png
wKg0C2UZbkaAU4iAACV0MEi96U271.png
wKg0C2UZbm6AcBthAACWJOYgr7I431.png
wKg0C2UZboeAWJgYAACEJMUzF7g096.png2.3 切面
2.4 在 Service 层实施鉴权
三、鉴权措施的执行顺序
wKg0C2UZatyAVJXiAAByCYwBOc979.png四 、简单的垂直越权检测
wKg0C2UZayiAJMayAADNctgQos390.png