Spring的getRequestURI与getRequestURL方法导致安全风险 1. 测试代码 2. 绕过方式 3. 预防措施 推荐阅读

我们有时候会在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的路径;

推荐阅读

Tomcat URL解析差异性导致的安全问题

filter设计缺陷导致的权限绕过

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章