我们有时候会在Filter中对某些URL进行权限校验。若使用getRequestURI与getRequestURL方法获取URL,可能会导致权限绕过的风险。
漏洞原理:请求URL中可以填充一些特殊字符,来跳过权限校验。
1. 测试代码
权限验证器代码:
@Slf4j
@Service
public class URIRiskInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//校验数据
String requestURI = request.getRequestURI();
String requestURL = request.getRequestURL().toString();
String servletPath = request.getServletPath();
log.info("输出数据requestURI:{}\n,requestURL:{}\n,servletPath:{}\n", requestURI, requestURL, servletPath);
return true;
}
}
Controller层代码:
@Slf4j
@RestController
public class FirstController {
@RequestMapping(value = "/test")
public String test(@RequestParam("id") Long id, @RequestParam("name") String name) {
log.info("test,请求进来了");
return id + "-" + name + " is success";
}
}
2. 绕过方式
2.1 非标准化绕过
例如/system/login开头的接口是白名单,不需要进行访问控制(登陆页面所有人都可以访问),其他接⼝都需要进⾏登陆检查,防止未授权访问:
@Slf4j
@Service
public class URIRiskInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//校验数据
String requestURI = request.getRequestURI();
String requestURL = request.getRequestURL().toString();
String servletPath = request.getServletPath();
log.info("输出数据requestURI:{}\n,requestURL:{}\n,servletPath:{}\n", requestURI, requestURL, servletPath);
//放行登录请求
if (requestURI.startsWith("/system/login")) {
log.info("yes,跳过校验{}", requestURI);
} else {
log.info("no,进行校验{}", requestURI);
}
return true;
}
}
执行结果:
2021-12-07 16:13:23.114 INFO 11917 --- [nio-8080-exec-1] c.tellme.Interceptor.URIRiskInterceptor : 输出数据requestURI:/system/login/../../test
,requestURL:http://localhost:8080/system/login/../../test
,servletPath:/test
2021-12-07 16:13:23.115 INFO 11917 --- [nio-8080-exec-1] c.tellme.Interceptor.URIRiskInterceptor : yes,跳过校验/system/login/../../test
2021-12-07 16:13:23.127 INFO 11917 --- [nio-8080-exec-1] com.tellme.controller.FirstController : test,请求进来了
可以看到:当访问http://localhost:8080/system/login/../../test
是可以访问到Controller方法的,且跳过了验证。
2.2 URL截断绕过
针对的是String.endsWith()
的权限校验。
@Slf4j
@Service
public class URIRiskInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//校验数据
String requestURI = request.getRequestURI();
String requestURL = request.getRequestURL().toString();
String servletPath = request.getServletPath();
log.info("输出数据requestURI:{}\n,requestURL:{}\n,servletPath:{}\n", requestURI, requestURL, servletPath);
//若结尾为.do或者.action的请求进行校验,但是SpringMVC很少有这种后缀的。
if (requestURI.endsWith("test")) {
log.info("no,进行校验{}", requestURI);
} else {
log.info("yes,跳过校验{}", requestURI);
}
return true;
}
}
测试结果:
2021-12-07 17:03:03.299 INFO 15815 --- [nio-8080-exec-1] c.tellme.Interceptor.URIRiskInterceptor : 输出数据requestURI:/test;123
,requestURL:http://localhost:8080/test;123
,servletPath:/test
2021-12-07 17:03:03.299 INFO 15815 --- [nio-8080-exec-1] c.tellme.Interceptor.URIRiskInterceptor : yes,跳过校验/test;123
2021-12-07 17:03:03.314 INFO 15815 --- [nio-8080-exec-1] com.tellme.controller.FirstController : test,请求进来了
可以看到:当访问http://localhost:8080/test;123
是可以访问到Controller方法的,且跳过了验证。
3. 预防措施
- 未设置context-path的项目使用getServletPath来代替getRequestURI;
- 在设置context-path的项目确定哪些api需要校验时,使用去掉context-path的路径;
网友评论