XSS解决方法(以及前段UrlEncode 加密两次的原理分析)

来源:互联网 发布:中医在线咨询软件 编辑:程序博客网 时间:2024/05/19 20:40
 XSSFilter.java
package com.sfpay.scfp.oms.app.filter;import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * Servlet Filter implementation class XSSFilter */public class XSSFilter implements Filter {    /**     * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)     */    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        if (!(request instanceof HttpServletRequest)) {            chain.doFilter(request, response);            return;        }        HttpServletRequest hrequest = (HttpServletRequest) request;        HttpServletResponse hresponse = (HttpServletResponse) response;        XSSRequestWrapper secureRequest = new XSSRequestWrapper(hrequest);        chain.doFilter(secureRequest, hresponse);    }    @Override    public void init(FilterConfig filterConfig) throws ServletException {        // TODO Auto-generated method stub    }    @Override    public void destroy() {        // TODO Auto-generated method stub    }}
XSSRequestWrapper.java
package com.sfpay.scfp.oms.app.filter;import java.net.URLDecoder;import java.util.Enumeration;import java.util.regex.Pattern;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;/** *  * @author wzs * */public class XSSRequestWrapper extends HttpServletRequestWrapper implements        HttpServletRequest {    HttpServletRequest orgRequest = null;    public XSSRequestWrapper(HttpServletRequest servletRequest) {        super(servletRequest);        orgRequest = servletRequest;    }    public boolean getStatus() {          boolean flag=true;          Enumeration<String> parameterNames = super.getParameterNames();         while(parameterNames.hasMoreElements()){             String nextElement = parameterNames.nextElement();             String parameter = this.getParameter(nextElement);              if(parameter.contains("script")){                 flag=false;                  break;              }          }         return flag;    }    @Override    public String[] getParameterValues(String parameter) {        String[] values = super.getParameterValues(parameter);        if (values == null) {            return null;        }        int count = values.length;        String[] encodedValues = new String[count];        for (int i = 0; i < count; i++) {            encodedValues[i] = cleanXSS(values[i]);        }        return encodedValues;    }    @Override    public String getParameter(String parameter) {        String value = super.getParameter(parameter);        if (value == null) {            return null;        }        return cleanXSS(value);    }    @Override    public String getHeader(String name) {        String value = super.getHeader(name);        if (value == null) {            return null;        }        return cleanXSS(value);    }    private String cleanXSS(String value) {        if (value == null || value.isEmpty()) {            return value;        }        // 后台在参数传递时已经进行了encode,此处需要decode。        /**         * 注意:前段js对url进行两次加密 UrlEncode(UrlEncode(URL)),目的是:前台第一次通过加密使用前后台约定编码。比如:UTF-8         *  加密完成以后,此时的url基本上就是%、字母、数字了。第二次加密就是对%、字母、数字进行加密了。此时无论使用哪一种编码集         *  加密后的结果都是一样的,(UTF-8/GBK/ISO-8859-1),         *   后台,只需要显示的调用一下URLDecoder.decode(value, "UTF-8"); 就可以了。         *   因为request.getParameter("name")之前会自动做一次解码的工作,而且是默认的ISO-8859-1。         *   然后通过显示的调用URLDecoder.decode(value, "UTF-8");就得到我们想要的url了         *   可参考:http://blog.csdn.net/kongqz/article/details/9028111         */        try {            value = URLDecoder.decode(value, "UTF-8");        } catch (Exception e) {            /*             *  显示模板保存时未进行encode,出现%时decode会抛异常,异常截取改为Exception且不打印堆栈。             * 当然,【%】encode后为【%25】,若值包含%25,则会decode为%。             *///          e.printStackTrace();        }        // 方案一:对相关Xss特殊字符进行替换,但是,返回前台时数据还是会有问题。        // You'll need to remove the spaces from the html entities below        // value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");        // value = value.replaceAll("\\(", "&#40;").replaceAll("\\)", "&#41;");        // value = value.replaceAll("'", "&#39;");        // value = value.replaceAll("eval\\((.*)\\)", "");        // value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");        // value = value.replaceAll("script", "scr1pt");        // 方案二:采用XssProject进行替换,有bug就是。实现原理是将Xss特殊字段截取。需要完整的html标签,无法只针对<script>...</script>进行处理,直接返回空字符串。//      StringReader reader = new StringReader(value);//      StringWriter writer = new StringWriter();//      try {//          HTMLParser.process(reader, writer, new XSSFilter(), true);//          value = writer.toString();//      } catch (NullPointerException e) {//          return value;//      } catch (Exception ex) {//          ex.printStackTrace(System.out);//      }        // 方案三:采用Spring提供的函数,跟方案一存在同样的bug。//      value = HtmlUtils.htmlEscape(value);        // 方案四:采用ESAPI进行处理。相关配置非常麻烦。        // NOTE: It's highly recommended to use the ESAPI library and uncomment the following line to avoid encoded attacks.        // value = ESAPI.encoder().canonicalize(value);        // 方案五:采用正则表达式,对相关Xss特殊字符进行替换。后台应该没有需要富文本编辑的功能。        // Avoid null characters        value = value.replaceAll("", "");        // Avoid anything between script tags        Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);        value = scriptPattern.matcher(value).replaceAll("");        // Avoid anything in a src='...' type of e­xpression        scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);        value = scriptPattern.matcher(value).replaceAll("");        scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);        value = scriptPattern.matcher(value).replaceAll("");        // Remove any lonesome </script> tag        scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);        value = scriptPattern.matcher(value).replaceAll("");        // Remove any lonesome <script ...> tag        scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);        value = scriptPattern.matcher(value).replaceAll("");        // Avoid eval(...) e­xpressions        scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);        value = scriptPattern.matcher(value).replaceAll("");        // Avoid e­xpression(...) e­xpressions        scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);        value = scriptPattern.matcher(value).replaceAll("");        // Avoid javascript:... e­xpressions        scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);        value = scriptPattern.matcher(value).replaceAll("");        // Avoid vbscript:... e­xpressions        scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);        value = scriptPattern.matcher(value).replaceAll("");        // Avoid onload= e­xpressions        scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);        value = scriptPattern.matcher(value).replaceAll("");        return value;    }    /**     * 获取最原始的request     *      * @return     */    public HttpServletRequest getOrgRequest() {        return orgRequest;    }    /**     * 获取最原始的request的静态方法     *      * @return     */    public static HttpServletRequest getOrgRequest(HttpServletRequest req) {        if (req instanceof XSSRequestWrapper) {            return ((XSSRequestWrapper) req).getOrgRequest();        }        return req;    }}

前段两次加密原理测试:可参考:http://blog.csdn.net/kongqz/article/details/9028111

String sArgs="与客户合同中约定利率为12%,系统显示利率为36.4%,故需减免复利差额";        //第一次加密,核心加密        String encode = URLEncoder.encode(sArgs, "UTF-8");        System.out.println(encode);        //第二次加密,只是对%、字母、数字进行二次加密。使用任何字符集加密的结果是一样的        String encode2 = URLEncoder.encode(encode, "ISO-8859-1");        System.out.println(encode2);        System.out.println(".......................................");        //第一次解密:对第二次加密的结果进行解密        String decode = URLDecoder.decode(encode2, "UTF-8");        System.out.println(decode);        //第二次解密:核心解密        String decode2 = URLDecoder.decode(decode, "UTF-8");        System.out.println(decode2);

对%、字母、数字进行二次加密,使用任何字符集加密的结果是一样的。测试如下;

String sArgs="与客户合同中约定利率为12%,系统显示利率为36.4%,故需减免复利差额";        //第一次加密,核心加密        String encode = URLEncoder.encode(sArgs, "UTF-8");        System.out.println(encode);         //str 就是 encode 的值        String str ="%E4%B8%8E%E5%AE%A2%E6%88%B7%E5%90%88%E5%90%8C%E4%B8%AD%E7%BA%A6%E5%AE%9A%E5%88%A9%E7%8E%87%E4%B8%BA12%25%EF%BC%8C%E7%B3%BB%E7%BB%9F%E6%98%BE%E7%A4%BA%E5%88%A9%E7%8E%87%E4%B8%BA36.4%25%EF%BC%8C%E6%95%85%E9%9C%80%E5%87%8F%E5%85%8D%E5%A4%8D%E5%88%A9%E5%B7%AE%E9%A2%9D";        String decode = URLEncoder.encode(str, "UTF-8");        System.out.println(decode);        String decode2 = URLEncoder.encode(str, "GBK");        System.out.println(decode2);        String decode3=  URLEncoder.encode(str, "ISO-8859-1");        System.out.println(decode3);        //decode decode2 decode3 的结果是一样的
原创粉丝点击