• getRequestURI 导致的安全问题


    getRequestURI 导致的安全问题

    上图是现在最常见的web架构。

    HttpServletRequest 的几个API

    @ResponseBody
    @RequestMapping(value = "index")
    public String index(HttpServletRequest req){
    
        String requestURL = req.getRequestURL().toString();
        String requestURI = req.getRequestURI();
        String contextPath = req.getContextPath();
        String servletPath = req.getServletPath();
    
        return "getRequestURL: " + requestURL + "\n" +
            "getRequestURI: " + requestURI + "\n" +
            "getServletPath: " + servletPath;
    }
    

    首先我们搭建一个web服务来测试下这几个api。

    相关版本信息:

    • apache-tomcat-8.5.56
    • nginx-1.20.2

    browser -> tomcat

    经过测试有如下结果

    payload getRequestURL getRequestURI getServletPath
    /index http://127.0.0.1:8081/index /index /index
    /./index http://127.0.0.1:8081/./index /./index /index
    /.;/index http://127.0.0.1:8081/.;/index /.;/index /index
    /a/../index http://127.0.0.1:8081/a/../index /a/../index /index
    /a/..;/index http://127.0.0.1:8081/a/..;/index /a/..;/index /index
    /;/index http://127.0.0.1:8081/;/index /;/index /index
    /;a/index http://127.0.0.1:8081/;a/index /;a/index /index
    /%2e/index http://127.0.0.1:8081/./index /%2e/index /index
    /inde%78 http://127.0.0.1:8081/index /inde%78 /index

    可以看出 getServletPath 会对获取的字符进行url解码

    browser -> nginx -> tomcat

    这里要分两种情况,proxy_pass 结尾带斜杠和结尾不带斜杠。

    proxy_pass 结尾不带斜杠

    nginx 配置如下

        upstream tomcat {
            server 127.0.0.1:8081;
        }
    
        server {
            listen       80;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                proxy_pass http://tomcat;  #结尾不带斜杠
                root   html;
                index  index.html index.htm;
            }
        }
    

    测试结果如下

    payload getRequestURL getRequestURI getServletPath
    /index http://tomcat/index /index /index
    /./index http://tomcat/./index /./index /index
    /.;/index http://tomcat/.;/index /.;/index /index
    /a/../index http://tomcat/a/../index /a/../index /index
    /a/..;/index http://tomcat/a/..;/index /a/..;/index /index
    /;/index http://tomcat/;/index /;/index /index
    /;a/index http://tomcat/;a/index /;a/index /index
    /%2e/index http://tomcat/./index /%2e/index /index
    /inde%78 http://tomcat/index /inde%78 /index

    看起来跟上面 browser -> tomcat 没什么区别

    proxy_pass 结尾带斜杠

    upstream tomcat {
            server 127.0.0.1:8081;
        }
    
    server {
            listen       81;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                proxy_pass http://tomcat/;  #结尾带斜杠
                root   html;
                index  index.html index.htm;
            }
        }
    
    payload getRequestURL getRequestURI getServletPath
    /index http://tomcat/index /index /index
    /./index http://tomcat/index /index /index
    /.;/index http://tomcat/.;/index /.;/index /index
    /a/../index http://tomcat/index /index /index
    /a/..;/index http://tomcat/a/..;/index /a/..;/index /index
    /;/index http://tomcat/;/index /;/index /index
    /;a/index http://tomcat/;a/index /;a/index /index
    /%2e/index http://tomcat/index /index /index
    /inde%78 http://tomcat/index /index /index

    通过对比可以看出proxy_pass 结尾带斜杠nginx会做如下处理

    • 将 ../ ./ 进行规范化处理,转成绝对路径
    • 会进行url解码

    写法问题

    日常工作中经常看到开发这样对url进行权限控制

    比如,api, login 是不需要无限制访问的,admin是需要权限访问的

        @ResponseBody
        @RequestMapping(value = "api")
        public String api(){
            return "Api Page";
        }
    
        @ResponseBody
        @RequestMapping(value = "login")
        public String login(){
            return "Login Page";
        }
    
        @ResponseBody
        @RequestMapping(value = "admin")
        public String admin(){
            return "Admin Page";
        }
    
    public class MyFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            //白名单url
            String[] whiteUrl = new String[]{"/api","/login","/index"};
    
            String uri = httpServletRequest.getRequestURI();
            boolean doFilter = true;
            for (int i = 0; i < whiteUrl.length; i++) {
                if(uri.startsWith(whiteUrl[i])){
                    doFilter = false;
                    break;
                }
            }
    
            if(doFilter){
                httpServletResponse.sendRedirect("/login");
            }else{
                filterChain.doFilter(httpServletRequest, httpServletResponse);
            }
        }
    }
    
    <filter>
        <filter-name>myFilter</filter-name>
        <filter-class>com.test.filter.MyFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    可以看到上面使用了 getRequestURI 来获取uri ,这种获取uri的方式会被绕过。

    总结

    getRequestURL()getRequestURI()这两个API解析的URL是包含特殊字符的,当使用不当时会存在安全问题,我们应该进行使用getServletPath() 来获取URI。

  • 相关阅读:
    mysql source命令可以导入比较大的文件
    开源 小程序
    React-Native项目在Android真机上调试
    react-native中长度单位换算
    webpack 去console
    微信H5移动端真机调试--vConsole
    记录
    盘点ES7、ES8、ES9、ES10新特性
    Mach-o可执行文件简述
    堆排序算法
  • 原文地址:https://www.cnblogs.com/depycode/p/16124191.html
Copyright © 2020-2023  润新知