springmvc使用过滤器filter实现过滤XSS,SQL脚本等功能,然后在初始化init方法加载需要过滤的XSS,SQL脚本配置,
package com.csair.csm.web.filter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
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 com.csair.csm.util.ParamRequestWrapper;
public class XssFilter implements Filter {
private String interceptStr = null;
private List<String> interceptArr = new ArrayList<String>();
private String validateStr = null;
private List<String> validateArr = new ArrayList<String>();
private String sqlInterceptArrStr = null;
private List<String> sqlInterceptArr = new ArrayList<String>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
interceptStr = filterConfig.getInitParameter("intercept");
interceptArr = Arrays.asList(interceptStr.split(";"));
sqlInterceptArrStr = filterConfig.getInitParameter("sqlValidate");
sqlInterceptArr = Arrays.asList(sqlInterceptArrStr.split("\\|"));
validateStr = filterConfig.getInitParameter("validate");
validateArr = Arrays.asList(validateStr.split("\\|"));
for (String item : validateArr) {
ParamRequestWrapper.patternArr.add(Pattern.compile(item.toLowerCase()));
}
ParamRequestWrapper.validateArr = this.validateArr;
ParamRequestWrapper.interceptArr = this.interceptArr;
ParamRequestWrapper.sqlInterceptArr = this.sqlInterceptArr;
}
/**
*
* @see javax.servlet.Filter#destroy()
*/
@Override
public void destroy() {}
/**
* @param arg0
* @param arg1
* @param arg2
* @throws IOException
* @throws ServletException
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
* javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
// 解决post中文乱码问题
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(new ParamRequestWrapper(servletRequest), response);
}
}
ParamRequestWrapper.java类如下
package com.csair.csm.util;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class ParamRequestWrapper extends HttpServletRequestWrapper {
public static List<Pattern> patternArr = new ArrayList<Pattern>();
public static List<String> validateArr = new ArrayList<String>();
public static List<String> sqlInterceptArr = new ArrayList<String>();
public static List<String> interceptArr = new ArrayList<String>();
private HttpServletRequest request;
private final byte[] body; // 报文
public ParamRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.request = request;
body = readBytes(request.getInputStream());
}
private byte[] readBytes(InputStream in) throws IOException {
BufferedInputStream bufin = new BufferedInputStream(in);
int buffSize = 1024;
ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize);
byte[] temp = new byte[buffSize];
int size = 0;
while ((size = bufin.read(temp)) != -1) {
out.write(temp, 0, size);
}
bufin.close();
byte[] content = out.toByteArray();
return content;
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
private String format(String name) {
String value = replaceParams(name);
if (!this.request.getMethod().equals("GET")) {// 判断是否是get请求方式
return value;
}
String result = null;
try {
result = new String(value.getBytes("utf-8"), this.request.getCharacterEncoding());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
public boolean checkIsIntercept() {
String url = request.getRequestURL().toString();
boolean flag = false;
for (String item : interceptArr) {
if (url.contains(item)) {
flag = true;
break;
}
}
return flag;
}
public String replaceParams(String value) {
String formatStr = XSSUtil.escape(value);
if (checkIsIntercept()) {
formatStr = formatStr.replaceAll("\"", """);
}
for (String item : validateArr) {
Matcher matcher = WebUtil.getRegexNormalMatcher(item, formatStr);
formatStr = matcher.replaceAll("");
}
return formatStr;
}
/**
*
* @param name
* @return
*/
public Object getAttribute(String name) {
Object value = super.getAttribute(name);
if (value instanceof String) {
value = format(String.valueOf(value));
}
return value;
}
/**
* 重写getParameter方法
*
* @param name
* @return
*/
public String getParameter(String name) {
String value = super.getParameter(name);
if (value == null)
return null;
return format(value);
}
/**
*
* @param name
* @return
*/
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values != null) {
for (int i = 0; i < values.length; i++) {
values[i] = format(values[i]);
}
}
return values;
}
/**
* @return
*/
public Map<String, String[]> getParameterMap() {
Map<String, String[]> paramMap = (Map<String, String[]>) super.getParameterMap();
Map<String, String[]> resultMap = new HashMap<String, String[]>();
for (Iterator iterator = paramMap.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) iterator.next();
String[] values = entry.getValue();
for (int i = 0; i < values.length; i++) {
if (values[i] instanceof String) {
values[i] = format(values[i]);
}
}
resultMap.put(replaceParams(entry.getKey()), values);
}
return resultMap;
}
}
如果我们的代码程序是用流来接收参数对象,以上的参数过滤封装并不会生效,解决方案,可以在过滤器获取流的时候,作处理,代码如下
public class WrapperRequestBody extends HttpServletRequestWrapper {
public static List<Pattern> patternArr = new ArrayList<Pattern>();
public static List<String> validateArr = new ArrayList<String>();
private final String body;
public WrapperRequestBody(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
body = format(stringBuilder.toString());
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
private String format(String value) {
if(StringUtils.isEmpty(value)){
return value;
}
return replaceParams(value);
}
public String replaceParams(String value) {
for (String item : validateArr) {
Matcher matcher = WebUtil.getRegexNormalMatcher(item, value);
value = matcher.replaceAll("");
}
return value;
}
XssUtils.java工具类如下
package com.csair.csm.util;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XssUtils {
private static final Logger logger = LoggerFactory.getLogger(XssUtils.class);
/**
* 将特殊字符转换为对应的实体引用或字符引用。
*
* @throws IOException
* @throws IOException
*/
public static String toHtml(String url, String str) {
return toHtml2(url, str);
}
/**
* 字符转译,对;单引号,(,) 还有斜杠进行转译成原生字符 例如(;)+;将变成原生的字符;
*
* @param str
* @param enc
* @return 转译后的字符串
*/
public static String urlDecoderDecode(String str, String enc) {
str = str.replaceAll("(;)+;", ";");
str = str.replaceAll("'", "\'");
str = str.replaceAll("(", "(");
str = str.replaceAll(")", ")");
str = str.replaceAll(""", "\"");
str = str.replaceAll("/", "/");
try {
str = URLDecoder.decode(str, enc);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("URLDecode转换出错");
}
return str;
}
/**
* 将特殊字符转换为对应的实体引用或字符引用。
*/
private static String toHtml2(String url, String str) {
if (str == null) {
return null;
}
StringBuffer sb = new StringBuffer();
boolean hasXssChar = false;
int len = str.length();
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
switch (c) {
/*
* case '\'': sb.append("'"); hasXssChar = true; break; case '\"': sb.append(""");
* hasXssChar = true; break;
*/
case '<':
sb.append("<");
hasXssChar = true;
break;
case '>':
sb.append(">");
hasXssChar = true;
break;
case '(':
sb.append("(");
hasXssChar = true;
break;
case ')':
hasXssChar = true;
sb.append(")");
break;
/*
* case ';': sb.append(";"); hasXssChar = true; break; case '/': sb.append("/");
* hasXssChar = true; break;
*/
case '\\':
sb.append("\");
hasXssChar = true;
break;
default:
sb.append(c);
}
}
if (hasXssChar) {
logger.debug(url + "进行Xss检查,已进行替换, 原始输入数据为:" + str);
}
return sb.toString().replaceAll("script", "").replaceAll("eval\\((.*)\\)", "");
// return sb.toString();
}
@SuppressWarnings("unused")
private static void logTime(long sTime) {
if ((System.currentTimeMillis() - sTime) > 100) {
logger.debug("转义耗时:" + (System.currentTimeMillis() - sTime));
}
}
}
WebUtils.java工具类如下
package com.csair.csm.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WebUtil {
public static Matcher getRegexNormalMatcher(String regex, String targetStr) {
Pattern compile = Pattern.compile( regex ,Pattern.CASE_INSENSITIVE);
Matcher matcher = compile.matcher(targetStr);
return matcher;
}
}
最后一步,即在WEB.XML配置过滤器指向我们定义的filter,并且配置过滤XSS,SQL脚本的关键字
<filter>
<filter-name>xssFilter</filter-name>
<filter-class>com.csair.csm.web.filter.XssFilter</filter-class>
<init-param>
<param-name>intercept</param-name>
<param-value>product/search;order/verify;manage/catalog/view;order/myorder;ehome/act</param-value>
</init-param>
<init-param>
<param-name>validate</param-name>
<param-value><![CDATA[font-family|show|class=|.source|eval|expression|onmouse|onmouseover|javascript|confirm|meta|onblur|window.|document|marquee|location|onfocus|onchange|onclick|onkey|onmousedown|prompt|SYS.TAB|script|alert|iframe|frame|href=|<|>|\\x|%5Cx|\*/|/\*|vbscript:|view-source:|.location|.cookie|oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload]]></param-value>
</init-param>
<init-param>
<param-name>sqlValidate</param-name>
<param-value><![CDATA[where|or|and|drop|delete]]></param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>xssFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>