最近被安保测评搞得头疼,本来网站上有XSS处理,代码也是参考网络,结果发现不好使。此处呵呵了。。。。 一般情况下百度出来的结果都是不好用的例子,虽然代码还挺全面,就是拦不住你说气人不气人。
下面发一个经过我实际应用好使的XSS过滤器,帮大家节省时间
/**
* 安全的Filter(过滤SQL和XSS)
*
*
* <!-- 解决xss & sql漏洞 -->
<filter>
<filter-name>SafeFilter</filter-name>
<filter-class>cn.he.xss.HttpServletRequestSafeFilter</filter-class>
<init-param>
<param-name>filterXSS</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>filterSQL</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SafeFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
*
*/
public class HttpServletRequestSafeFilter implements Filter{
public static final String FILTER_XSS = "filterXSS";
public static final String FILTER_SQL = "filterSQL";
FilterConfig filterConfig = null;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void destroy() {
this.filterConfig = null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
boolean filterXSS = false;
boolean filterSQL = false;
if (StringUtils.isNotEmpty(filterConfig.getInitParameter(FILTER_XSS))) {
filterXSS = Boolean.valueOf(filterConfig.getInitParameter(FILTER_XSS));
}
if (StringUtils.isNotEmpty(filterConfig.getInitParameter(FILTER_SQL))) {
filterSQL = Boolean.valueOf(filterConfig.getInitParameter(FILTER_SQL));
}
chain.doFilter(new HttpServletRequestSafeWrapper((HttpServletRequest) request, filterXSS, filterSQL), response);
}
}
/**
* 安全的HttpServlet(过滤SQL和XSS)
*
*/
public class HttpServletRequestSafeWrapper extends HttpServletRequestWrapper{
private boolean filterXSS = true;
private boolean filterSQL = true;
private HttpServletRequest orgRequest = null;
public HttpServletRequestSafeWrapper(HttpServletRequest request) {
super(request);
this.orgRequest = request;
}
public HttpServletRequestSafeWrapper(HttpServletRequest request, boolean filterXSS, boolean filterSQL) {
super(request);
orgRequest = request;
this.filterXSS = filterXSS;
this.filterSQL = filterSQL;
}
@Override
public Enumeration<String> getParameterNames() {
Set<String> parameterNameSafeList = Sets.newHashSet();
Enumeration parameterNames = super.getParameterNames();
while (parameterNames.hasMoreElements()) {
parameterNameSafeList.add(filterText(String.valueOf(parameterNames.nextElement()), true));
}
return Collections.enumeration(parameterNameSafeList);
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> parameterSafeMap = Maps.newHashMap();
Set parameterNameSet = super.getParameterMap().keySet();
for (Object key : parameterNameSet) {
parameterSafeMap.put(String.valueOf(key), getParameterValues(String.valueOf(key)));
}
return parameterSafeMap;
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(filterText(name, true));
if (values == null || values.length == 0) {
return null;
}
int count = values.length;
String[] safeValues = new String[count];
for (int i = 0; i < count; i++) {
// 判断是否为JSON格式数据
if (isJsonFormat(values[i])) {
safeValues[i] = filterText(values[i], false);
} else {
safeValues[i] = filterText(values[i], true);
}
}
return safeValues;
}
/**
* 覆盖getParameter方法,将参数名和参数值都做xss & sql过滤。<br/>
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(filterText(name, true));
String safeValue = null;
if (StringUtils.isNotEmpty(value)) {
// 判断是否为JSON格式数据
if (isJsonFormat(value)) {
safeValue = filterText(value, false);
} else {
safeValue = filterText(value, true);
}
}
return safeValue;
}
/**
* 覆盖getHeader方法,将参数名和参数值都做xss & sql过滤。<br/>
* 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
* getHeaderNames 也可能需要覆盖
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(filterText(name, true));
String safeValue = null;
if (StringUtils.isNotEmpty(value)) {
// 判断是否为JSON格式数据
if (isJsonFormat(value)) {
safeValue = filterText(value, false);
} else {
safeValue = filterText(value, true);
}
}
return safeValue;
}
@Override
public Enumeration<String> getHeaders(String name) {
Set<String> headerValSafeList = Sets.newHashSet();
Enumeration headerVals = super.getHeaders(name);
while (headerVals.hasMoreElements()) {
headerValSafeList.add(filterText(String.valueOf(headerVals.nextElement()), true));
}
return Collections.enumeration(headerValSafeList);
}
@Override
public Enumeration<String> getHeaderNames() {
Set<String> headerNamesSafeList = Sets.newHashSet();
Enumeration headerNames = super.getHeaderNames();
while (headerNames.hasMoreElements()) {
headerNamesSafeList.add(filterText(String.valueOf(headerNames.nextElement()), true));
}
return Collections.enumeration(headerNamesSafeList);
}
/**
* 判断是否为JSON格式
*
* @param json
* @return
*/
private boolean isJsonFormat(String json) {
return StringUtils.isNotEmpty(json) && json.startsWith("{") && json.endsWith("}");
}
/**
* 过滤XSS和SQL
*
* @param text
* @param isHtmlEscape
* @return
*/
private String filterText(final String text, final boolean isHtmlEscape) {
String filterText = StringUtils.isEmpty(text)?null:text.trim();
if (filterXSS) {
filterText = cleanXSS(filterText);
}
if (filterSQL) {
filterText = stripSqlInjection(filterText);
}
if (isHtmlEscape) {
filterText = HtmlUtils.htmlEscape(filterText);
}
return filterText;
}
/**
* 获取最原始的request
*
* @return
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取最原始的request的静态方法
*
* @return
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if (req instanceof HttpServletRequestSafeWrapper) {
return ((HttpServletRequestSafeWrapper) req).getOrgRequest();
}
return req;
}
private static final List<Pattern> XSS_PATTERN_LIST = Lists.newArrayList(Pattern.compile("<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE),
Pattern.compile("<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<+\\s*\\w*\\s*(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|onload|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)+\\s*=+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
/**
* @param value 待处理内容
* @return
* @Description 过滤XSS脚本内容
*/
private static String cleanXSS(String value) {
if (StringUtils.isNotBlank(value)) {
Matcher matcher;
for (Pattern pattern : XSS_PATTERN_LIST) {
matcher = pattern.matcher(value);
if (matcher.find()) {
value = matcher.replaceAll("");//将带有尖括号的脚本设置为空。在我的框架里如果是转码,还是会报没有相应的属性(尤其是基于hibernate)的情况,所以直接设置成空
}
}
value = value.replaceAll("<", "<").replaceAll(">", ">");
}
return value;
}
/**
* @param value 待处理内容
* @return
* @Description 过滤SQL注入内容
*/
private static String stripSqlInjection(String value) {
return StringUtils.isEmpty(value)? null : value.replaceAll("('.+--)|(--)|(%7C)", "");
}
}
代码已全部奉上,快试一试吧
补充:
String filterText = StringUtils.isEmpty(text)?null:text.trim(); 这行代码改成下面代码,这个bug巨坑,使用下面的写法是保持原有的参数不变
String filterText = StringUtils.isEmpty(text)?text:text.trim();//第一个条件 text是NULL还是空,保持原样