越权测试中的痛点/难点
越权漏洞是日常开发中比较常见的一个缺陷。要进行越权检测,一般需要明确定义和管理系统中的权限。这可能包括用户角色、资源和操作的细粒度权限控制。维护这些权限定义并确保它们与实际业务操作一致本身就是是一项复杂的任务。
以垂直越权为例,一般测试时,首先会获取到高权限用户模块的业务数据包,然后在BurpSuite将其鉴权凭证(一般是cookie)替换成低权限用户的,也可以构造好高权限的业务数据包,然后在浏览器登陆低权限用户,通过访问相关的数据包,若业务依旧可以成功访问,则存在垂直越权缺陷。但是实际测试过程中可能会遇到一系列的问题:
-
在测试过程中,了解应用程序的请求接口和其参数是至关重要的。这包括了解哪些参数控制了访问权限、哪些参数可以被滥用,以及如何构造恶意请求。但是在实际测试时,经常是通过js代码审计发现潜在的垂直越权接口,请求参数信息可能不完整或模糊导致没办法进步一测试(这些信息可能不容易获取,因为无法获取应用程序的源代码,也无法直接查看其接口文档)。 -
一些越权漏洞可能导致误删除/修改/更新操作,在没有测试环境的情况下数据恢复比较困难。 -
在黑盒扫描中,需要获取足够的应用程序流量数据,以便进行分析和检测。然而,流量可能受到加密、访问权限和网络配置的限制。
除此以外,还有还多其他的因素,下面先看看SpringWeb中常见鉴权措施与解析顺序,简单探索下能不能在一定程度上解决上述的问题。
SpringWeb中常见鉴权措施
2.1 过滤器Filter
public FilterRegistrationBean<AuthFilter> FilterConfig() {
FilterRegistrationBean<AuthFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new AuthFilter());
registrationBean.addUrlPatterns("/admin/*");
return registrationBean;
}
假设/admin存在如下更新用户的接口:
public ApiResponse<User> addUser ( User user){
return new ApiResponse<>(200, "Success", userService.saveOrUpdate(user));
}
该接口使用POST进行请求,通过@RequestBody的方式进行数据的传输。未经过任何类似鉴权filter方式处理的话,若通过如下方式进行请求会返回相应的状态码:
-
当使用GET方式请求该接口时,会返回405 Status
-
当通过 application/x-www-form-urlencoded;charset=UTF-8方式请求该接口时,会返回415 Status
-
当通过application/json方式请求,但是传递参数为null时,会返回400 Status:
2.2 拦截器Interceptor( preHandle )
-
preHandle:Controller方法执行之前执行preHandle(),返回值是一个boolean,表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法 -
postHandle:Controller方法执行之后执行postHandle() -
afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
在鉴权场景中,拦截器常常用于在请求处理前执行,也就是一般会调用preHandle,后续仅讨论preHandle的情况 。
public class AuthInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在请求处理前执行,可进行鉴权逻辑
// 获取当前用户信息
String currentUser = getCurrentUser(request);
// 进行垂直越权检测
if (!hasPermission(currentUser)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return false; // 拒绝访问
}
return true; // 允许访问
}
private String getCurrentUser(HttpServletRequest request) {
// 实现获取当前用户的逻辑
}
private boolean hasPermission(String currentUser, String resourceId) {
// 实现垂直越权检测逻辑
}
}
在配置类添加@Configuration注解,通过重写addInterceptors方法,添加拦截器,并配置匹配路径,AuthInterceptor对/admin/目录下的所有资源都生效,也就是说这是一个垂直鉴权的措施:
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器
registry.addInterceptor(new AuthInterceptor(environment)).addPathPatterns("/admin/**").excludePathPatterns("/test/**");
}
同样是前面过滤器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的匹配,若资源不存在的话,也没必要进一步处理了:
2.3 切面
切面是Spring的AOP(面向切面编程)组件,用于定义横切关注点。虽然它们通常用于横切关注点的日志记录、性能监控等,但也可以用于鉴权。你可以创建自定义切面,它们可以拦截方法调用,以执行鉴权逻辑。 相比其他措施, 切面的执行顺序十分灵活,可以通过配置进行管理,它们可以被指定在其他组件之前或之后执行 。 最常见的是通过注解方式来实现,主要包含以下注解:
-
@Aspect :指定切面类(可以通过检索@Aspect快速定位切面类) -
@Pointcut :定义了相应的 Advice(具体要做的操作)触发的地方。一般是通过通配符、正则表达式等方式。
"@annotation(Auth)") (
public void requirePermissionAuth() {
-
通知(Advice)类型:定义要执行的方法,安全中一般是鉴权 -
@Before: 在目标方法执行之前,执行注解标记的内容 -
@After: 在目标方法执行之后,执行注解标记的内容 -
@AfterReturning: 在目标方法返回后,执行注解标记的内容 -
@AfterThrowing: 在目标方法抛出异常后,执行注解标记的内容 -
@Around: 在目标方法执行前后,分别执行对应的内容
2.4 在 Service 层实施鉴权
public class UserService {
public boolean hasPermissionToDeleteUser(User currentUser, User userToDelete) {
// 自定义鉴权逻辑,检查是否允许当前用户删除指定用户
if (currentUser.isAdmin()) {
return true; // 管理员可以删除任何用户
} else {
return currentUser.getId().equals(userToDelete.getId()); // 用户只能删除自己
}
}
public User deleteUser(User currentUser, User userToDelete) {
if (hasPermissionToDeleteUser(currentUser, userToDelete)) {
// 执行删除用户的业务逻辑
return userToDelete;
} else {
throw new SecurityException("没有权限删除用户");
}
}
// 其他方法...
}
因为Service的调用一般都是在Controller,此时已经完成路径解析&匹配了,相比于前面的措施,基本上是最后才进行解析。
鉴权措施的执行顺序
根据上面的分析,可以大概知道,当一个请求到达时,执行顺序是:Filter过滤器> Interceptor拦截器> ControllerAdvice > AOP > Controller,在Controller之后,就是具体的service调用了。
简单的垂直越权监测
对于垂直越权的场景,一般情况下在系统设计开发时,会根据路由进行权限角色的区分,过拦截器(Interceptor)或过滤器(Filter)之类的中间件组件来保护应用程序。 以SpringSecurity为例,Spring Security内部其实是通过一个过滤器链来实现认证/鉴权等流程的。例如下面的例子,这里限制了/admin/以及/manage目录下的接口均需要ADMIN角色才能进行访问: public class SpringSecurityConfig {
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().requestMatchers("/admin/**","/manage/**").hasRole("ADMIN").anyRequest().permitAll();
return http.build();
}
}
根据前面的分析,也就是说不论/admin下的接口参数是否已知,是否有完整的请求流量,都可以通过直接请求接口的方式进行验证,若返回接口不是403,则可能存在垂直越权的风险:
对于基于Spring开发系统,可以结合鉴权措施的执行顺序,在一定程度上解决垂直越权测试上的问题,但是对于平行越权来说,一般情况下的防护措施是基于业务逻辑的,一般会在 Service 层实现鉴权。这里就必须获取到对应的请求参数了或者完整的流量了。 往期推荐
原文始发于微信公众号(SecIN技术平台):原创 | SpringWeb常见鉴权措施与垂直越权检测
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论