发布于 

url动态加解密

表单提交(或其他需要请求后台url)的场景下,url对于客户端来说是可见的,攻击者可以通过url猜测到很多信息并伪造请求对url进行攻击。本文探讨了一种对
url加密和解密的方案,并给出了关键代码。

Form表单提交

  1. 页面form的action地址加密:

    先在原始url之前添加encoded/(或其他)以方便后台认证是否合法url;
    在加密后的url地址后加.do(或其他)以方便filter过滤。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <%
    String url = DesUtil.encrypt("encoded/test/second.htm");
    %>
    <form action="<%=url%>.do" method="post">
    <input type="text" id="account" name="j_username" class="text " placeholder="请输入手机号/邮箱"
    value="tangliu"/>
    <input type="text" id="password" name="j_password" class="text " placeholder="请输入密码"
    value="123456"/>
    <input type="submit" value="Submit" />
    </form>
  2. 自定义一个filter,放在其他filter之前:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <filter>
    <filter-name>decodeFilter</filter-name>
    <filter-class>com.tangliu.test.encodeurl.DecodeUrlFilter</filter-class>
    <async-supported>true</async-supported>
    </filter>
    <filter-mapping>
    <filter-name>decodeFilter</filter-name>
    <url-pattern>*.do</url-pattern>
    </filter-mapping>
  3. filter中对url进行解密,若解密后的url不符合规范(不是以”encoded/“开头),则抛弃,否则对url请求进行forward转发。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
    try {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    String encodedUrl = httpRequest.getRequestURI();
    encodedUrl = encodedUrl.substring(1, (encodedUrl.length() - 3));

    String unencodedUrl = DesUtil.decrypt(encodedUrl);
    //如果解密后不是以encoded开头则说明不是合法请求,抛弃
    if(!unencodedUrl.startsWith("encoded"))
    return;

    unencodedUrl = unencodedUrl.replace("encoded/", "");
    unencodedUrl = httpRequest.getContextPath() + unencodedUrl;
    request.getRequestDispatcher("/" + unencodedUrl).forward(request, response);

    } catch (Exception e) {
    e.printStackTrace();
    }
    }
  4. 经过测试,froward转发的url可以被其他filter正确拦截,于此同时地址栏显示的是加密后的url,设置如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     <filter>
    <filter-name>testFilter</filter-name>
    <filter-class>com.tangliu.test.encodeurl.TestFilter</filter-class>
    <async-supported>true</async-supported>
    </filter>
    <filter-mapping>
    <filter-name>testFilter</filter-name>
    <url-pattern>/test/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    其中<dispatcher>标签如果不设置,默认是REQUEST,表示拦截客户端请求,FORWARD表示拦截内部的forward请求。

  5. 同时最后收到请求的controller,能够正常接收request的参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @RequestMapping(value = "second", method = RequestMethod.POST)
    @ResponseBody
    public String service(HttpServletRequest request) {

    System.out.println(request.getParameter("j_username"));
    System.out.println(request.getParameter("j_password"));

    return "test";
    }

使用ajax post提交

  1. <meta>中写入加密后的url地址:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <meta name="formUrl" content="<%=DesUtil.encrypt("encoded/test/second.htm")%>.do">
    <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
    <script>
    function ajaxTest(){

    var url = $("meta[name='formUrl']").attr("content");
    var data = {};
    data['j_username'] = 'tangliu';
    data['j_password'] = '123456';
    $.post(url, data, function(result){
    alert(result);
    });

    }
    </script>
  2. <script>中写入加密后的url地址:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <script>
    function ajaxTest(){

    var url = "<%=DesUtil.encrypt("encoded/test/second.htm")%>.do";
    var data = {};
    data['j_username'] = 'tangliu';
    data['j_password'] = '123456';
    $.post(url, data, function(result){
    alert(result);
    });
    }
    </script>

测试可行。