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("<", "<").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标签,无法只针对<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 expression
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(...) expressions
scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid expression(...) expressions
scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid javascript:... expressions
scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid vbscript:... expressions
scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid onload= expressions
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 的结果是一样的