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

 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 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("<", "<").replaceAll(">", ">");
        // value = value.replaceAll("\\(", "(").replaceAll("\\)", ")");
        // value = value.replaceAll("'", "'");
        // value = value.replaceAll("eval\\((.*)\\)", "");
        // value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        // value = value.replaceAll("script", "scr1pt");

        // 方案二:采用XssProject进行替换,有bug就是。实现原理是将Xss特殊字段截取。需要完整的html标签,无法只针对进行处理,直接返回空字符串。
//      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("", 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  tag
        scriptPattern = Pattern.compile("", Pattern.CASE_INSENSITIVE);
        value = scriptPattern.matcher(value).replaceAll("");

        // Remove any lonesome 
                    
                    

你可能感兴趣的:(xss跨域脚本攻击,UrlEncode,加密两次,web安全)