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/%2e/index /%2e/index /index
/inde%78 http://127.0.0.1:8081/inde%78 /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/%2e/index /%2e/index /index
/inde%78 http://tomcat/inde%78 /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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM