公司做的一个web项目中有这个功能,记录并学习下。在web系统中很多页面有文本输入的功能,有些不严谨的程序,通过写一些特殊字符,js片段,sql脚步会导致程序出现bug,现在通过一个统一的功能进行屏蔽。主要通过过滤器、xml解析的机制实现。
1、首先在web.xml里面增加过滤器配置。
2、过滤器功能代码
3、哪些action需要进行过滤,具体的哪些字段需要过滤,通过xss-servlet-filter-rule.xml文件配置规则。
4、解析xss-servlet-filter-rule.xml文件相关的类。
1)、WebSecurityWrapper类
2)、WebXssEscapeFilterConfig类
3、WebXssEscapeFilterRule类
4)、WebXssEscapeFilterUtil工具类
5、默认的过滤规则接口类Defender.java
6、规则接口的实现类DefaultDefender.java
7、总结:实现思路主要通过过滤器将特殊字符进行过滤掉,在过滤器第一次执行的时候加载过滤规则并将保存到缓存中。
1、首先在web.xml里面增加过滤器配置。
<!-- 防sql注入和跨脚本攻击 -->
<filter>
<filter-name>webSecurityFilter</filter-name>
<filter-class>com.zhgl.filter.WebSecurityFilter</filter-class>
<init-param>
<param-name>filter_xss</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>filter_sql_injection</param-name>
<param-value>true</param-value>
</init-param>
</filter>
2、过滤器功能代码
/**
* project : yyzhglpt_public
* package :com.zhgl.filter
* file : WebSecurityFilter.java
*/
package com.zhgl.filter;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
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;
import com.yyzhglpt.util.StringUtil;
import com.zhgl.util.MessageStreamResult;
/**
* Specification : 文档说明
*/
public class WebSecurityFilter implements Filter
{
public static final String FILTER_XSS_PARAM_NAME = "filter_xss";
public static final String FILTER_SQL_INJECTION_PARAM_NAME = "filter_sql_injection";
boolean filterXSS = true;
boolean filterSQL = true;
@Override
public void destroy()
{
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
WebXssEscapeFilterUtil filterUtil = WebXssEscapeFilterUtil.getInstance();
String cutUrl = ((HttpServletRequest)request).getRequestURI();
String cutedUrl = cutUrl.substring(cutUrl.lastIndexOf("/") + 1);
String actionName = request.getParameter("action");
if (StringUtil.isNotEmpty(cutedUrl) && StringUtil.isNotEmpty(actionName))
{
WebXssEscapeFilterRule urlRule = filterUtil.getFilterRule(cutedUrl, actionName);
if (urlRule != null)
{
Map<String, String[]> paramMap = request.getParameterMap();
Set<String> entries = paramMap.keySet();
for (String entry : entries)
{
String paramName = entry;
String[] valueObj = paramMap.get(paramName);
if (urlRule.getParams().contains(paramName))
{
for(String str: valueObj){
if(!urlRule.getDefender().doFilter(str)){
if("html".equals(urlRule.getResultType())){
try
{
MessageStreamResult.msgStreamResult(response, "您输入的参数有非法字符,请输入正确的参数!");
return;
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}else if("json".equals(urlRule.getResultType())){
try
{
MessageStreamResult.msgStreamResult(response, "{\"msg\":\"您输入的参数有非法字符,请输入正确的参数!\",\"success\":false}");
return;
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// {\"msg\":\"\",\"success\":true}
}
}
}
}
}
}
//WebSecurityWrapper wrapper = new WebSecurityWrapper(servletRequest, WebXssEscapeFilterUtil.getInstance());
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void init(FilterConfig config)
throws ServletException
{
String filterXSSParam = config.getInitParameter(FILTER_XSS_PARAM_NAME);
String filterSQLParam = config.getInitParameter(FILTER_SQL_INJECTION_PARAM_NAME);
filterXSS = new Boolean(filterXSSParam);
filterSQL = new Boolean(filterSQLParam);
}
}
3、哪些action需要进行过滤,具体的哪些字段需要过滤,通过xss-servlet-filter-rule.xml文件配置规则。
<?xml version="1.0" encoding="UTF-8"?>
<config>
<!-- 默认过滤规则 -->
<default>
<defender>
<name>xssDefaultDefender</name>
<class>com.zhgl.filter.defender.DefaultDefender</class>
</defender>
</default>
<!-- url规则设置 -->
<url-rule-set>
<url-rule><!-- 一个xxx.do一个url-rule -->
<url>jxfa.do</url><!-- 拦截的URL -->
<actions>
<action name="saveFa" resultType="html"><!-- 具体的action名称可多个 请求链接的返回类型 html 为 普通的字符流 json为返回普通接送字符流-->
<params>
<param name="famc"/><!-- 需要拦截验证是否有特殊字段的字段 目前特殊字符为 < > ' " / & \ -->
<param name="fams"/><!-- 需要拦截验证是否有特殊字段的字段 目前特殊字符为 < > ' " / & \ -->
</params>
</action>
<action name="test" resultType="json">
<params>
<param name="query1" />
<param name="query2" />
<param name="query3" />
</params>
</action>
</actions>
</url-rule>
</url-rule-set>
</config>
4、解析xss-servlet-filter-rule.xml文件相关的类。
1)、WebSecurityWrapper类
/**
* Copyright 2014 winning, Inc. All rights reserved.
* project : yyzhglpt_public
* package :com.zhgl.filter
* file : WebSecurityWrapper.java
* date :2015-11-23
*/
package com.zhgl.filter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* Specification : 文档说明
*/
public class WebSecurityWrapper extends HttpServletRequestWrapper {
private static final String EVENTS = "((?i)onload|onunload|onchange|onsubmit|onreset" + "|onselect|onblur|onfocus|onkeydown|onkeypress|onkeyup"
+ "|onclick|ondblclick|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup)";
//private static final String XSS_HTML_TAG = "(%3C)|(%3E)|[<>]+|[']+|[\"]+|(%27)|(%22)|(%2527)";
//private static final String XSS_INJECTION = "((%22%20)|(%22\\s)|('%22)|(%22\\+))\\w.*|(\\s|%20)" + EVENTS + ".*|(%3D)";
//private static final String XSS_REGEX = XSS_HTML_TAG + "|" + XSS_INJECTION;
private static final String XSS_REGEX = "(%3C)|(%3E)|[<>]+";
private static final String SQL_REGEX = "";
boolean filterXSS = true;
boolean filterSQL = true;
public WebSecurityWrapper(HttpServletRequest request, boolean filterXSS, boolean filterSQL) {
super(request);
this.filterXSS = filterXSS;
this.filterSQL = filterSQL;
}
public WebSecurityWrapper(HttpServletRequest request) {
this(request,true,true);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return filterParamString(value);
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> rawMap = super.getParameterMap();
Map<String, String[]> filteredMap = new HashMap<String, String[]>(rawMap.size());
Set<String> keys = rawMap.keySet();
for (String key : keys) {
String[] rawValue = rawMap.get(key);
String[] filteredValue = filterStringArray(rawValue);
filteredMap.put(key, filteredValue);
}
return filteredMap;
}
protected String[] filterStringArray(String[] rawValue) {
String[] filteredArray = new String[rawValue.length];
for (int i = 0; i < rawValue.length; i++) {
filteredArray[i] = filterParamString(rawValue[i]);
}
return filteredArray;
}
@Override
public String[] getParameterValues(String name) {
String[] rawValues = super.getParameterValues(name);
if (rawValues == null)
return null;
String[] filteredValues = new String[rawValues.length];
for (int i = 0; i < rawValues.length; i++) {
filteredValues[i] = filterParamString(rawValues[i]);
}
return filteredValues;
}
protected String filterParamString(String rawValue) {
if (rawValue == null) {
return null;
}
if (filterXSS()) {
rawValue = rawValue.replaceAll(XSS_REGEX, "");
}
if (filterSQL()) {
rawValue = rawValue.replaceAll(SQL_REGEX, "");
}
return rawValue;
}
@Override
public Cookie[] getCookies() {
Cookie[] existingCookies = super.getCookies();
if(existingCookies != null) {
for(int i=0;i<existingCookies.length;++i) {
Cookie cookie = existingCookies[i];
cookie.setValue(filterParamString(cookie.getValue()));
}
}
return existingCookies;
}
@Override
public String getQueryString() {
return filterParamString(super.getQueryString());
}
protected boolean filterXSS() {
return filterXSS;
}
protected boolean filterSQL() {
return filterSQL;
}
}
2)、WebXssEscapeFilterConfig类
/*
* 文 件 名: WebXssEscapeFilterConfig.java
*/
package com.zhgl.filter;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.zhgl.filter.defender.Defender;
/**
* <一句话功能简述> <功能详细描述>
*
* @author 姓名 工号
* @version [版本号, 2015-12-2]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class WebXssEscapeFilterConfig
{
private static final String DEFAULT_FILTER_RULE_FILENAME = "xss-servlet-filter-rule.xml";
private Map<String, Map<String, WebXssEscapeFilterRule>> urlRuleSetMap = new HashMap();
private Defender defaultDefender = null;
public WebXssEscapeFilterConfig()
throws IllegalStateException
{
this(DEFAULT_FILTER_RULE_FILENAME);
}
public WebXssEscapeFilterConfig(String filename)
throws IllegalStateException
{
try
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
Element rootElement = builder.parse(is).getDocumentElement();
addDefaultInfo(rootElement);
addUrlRuleSet(rootElement);
System.out.println("配置文件初始化");
}
catch (Exception e)
{
throw new IllegalStateException("配置文件xss-servlet-filter-rule.xml读取异常.", e);
}
}
private void addDefaultInfo(Element rootElement)
{
NodeList nodeList = rootElement.getElementsByTagName("default");
if (nodeList.getLength() > 0)
{
Element defaultE = (Element)nodeList.item(0);
nodeList = defaultE.getElementsByTagName("defender");
if (nodeList.getLength() > 0)
{
String clazz = getTagContent((Element)nodeList.item(0), "class");
try
{
defaultDefender = (Defender)Class.forName(clazz.trim()).newInstance();
}
catch (Exception e)
{
throw new IllegalStateException("defender初始失败!", e);
}
}
}
}
private void addUrlRuleSet(Element rootElement)
{
NodeList nodeList = rootElement.getElementsByTagName("url-rule");
for (int i = 0; (nodeList.getLength() > 0) && (i < nodeList.getLength()); i++)
{
Element element = (Element)nodeList.item(i);
addUrlRule(element);
}
}
private void addUrlRule(Element element)
{
Map paramRuleMap = null;
String url = null;
Element actionsElement = null;
NodeList nodeList = element.getElementsByTagName("url");
if (nodeList.getLength() > 0)
{
url = nodeList.item(0).getTextContent();
}
if (url != null)
{
nodeList = element.getElementsByTagName("actions");
if (nodeList.getLength() > 0)
{
actionsElement = (Element)nodeList.item(0);
if (actionsElement != null)
{
Map<String,WebXssEscapeFilterRule> ls = addActionParamsRule(actionsElement);
if (ls.size() > 0)
{
urlRuleSetMap.put(url, ls);
}
}
}
}
}
private Map<String, WebXssEscapeFilterRule> addActionParamsRule(Element element)
{
Map<String, WebXssEscapeFilterRule> ls = new HashMap<String, WebXssEscapeFilterRule>();
NodeList nodeList = element.getElementsByTagName("action");
for (int i = 0; (nodeList.getLength() > 0) && (i < nodeList.getLength()); i++)
{
Element element_t = (Element)nodeList.item(i);
String actionName = element_t.getAttribute("name");
String resultType = element_t.getAttribute("resultType");
if (actionName == null || resultType == null)
{
continue;
}
List<String> params = new ArrayList<String>();
NodeList nodeListt = element_t.getElementsByTagName("params");
if (nodeListt.getLength() > 0)
{
Element element_params = (Element)nodeList.item(0);
NodeList nodeListtt = element_params.getElementsByTagName("param");
for (int k = 0; (nodeListtt.getLength() > 0) && (k < nodeListtt.getLength()); k++)
{
Element element_tt = (Element)nodeListtt.item(k);
String paramName = element_tt.getAttribute("name");
if (paramName == null)
{
continue;
}
params.add(paramName);
}
}
if (params.size() == 0)
{
continue;
}
WebXssEscapeFilterRule rule = new WebXssEscapeFilterRule();
rule.setActionName(actionName);
rule.setResultType(resultType);
rule.setParams(params);
rule.setDefender(defaultDefender);
ls.put(actionName, rule);
}
return ls;
}
private String getTagContent(Element eachElement, String tagName)
{
NodeList nodeList = eachElement.getElementsByTagName(tagName);
if (nodeList.getLength() > 0)
{
return nodeList.item(0).getTextContent();
}
return "";
}
public Defender getDefaultDefender()
{
return this.defaultDefender;
}
public WebXssEscapeFilterRule getUrlParamRule(String url, String actionName, String paramName)
{
Map<String,WebXssEscapeFilterRule> listRule = (Map<String,WebXssEscapeFilterRule>)urlRuleSetMap.get(url);
if(listRule == null){
return null;
}
if( listRule.get(actionName) == null){
return null;
}else{
WebXssEscapeFilterRule rule = listRule.get(actionName) ;
if( !rule.getParams().contains(paramName) ){
return null;
}else{
return rule;
}
}
}
public WebXssEscapeFilterRule getUrlParamRule(String url, String actionName)
{
Map<String,WebXssEscapeFilterRule> listRule = (Map<String,WebXssEscapeFilterRule>)urlRuleSetMap.get(url);
if(listRule == null){
return null;
}
if( listRule.get(actionName) == null){
return null;
}else{
WebXssEscapeFilterRule rule = listRule.get(actionName) ;
return rule;
}
}
}
3、WebXssEscapeFilterRule类
/*
* 文 件 名: WebXssEscapeFilterRule.java
*/
package com.zhgl.filter;
import java.util.List;
import com.zhgl.filter.defender.Defender;
/**
* <一句话功能简述> <功能详细描述>
*
* @author 姓名 工号
* @version [版本号, 2015-12-2]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class WebXssEscapeFilterRule
{
/**
* action名称
*/
private String actionName;
/**
* 返回类型
*/
private String resultType;
/**
* 拦截器
*/
private Defender defender;
/**
* 拦截的参数名称
*/
private List<String> params;
public String getActionName()
{
return actionName;
}
public void setActionName(String actionName)
{
this.actionName = actionName;
}
public String getResultType()
{
return resultType;
}
public void setResultType(String resultType)
{
this.resultType = resultType;
}
public List<String> getParams()
{
return params;
}
public void setParams(List<String> params)
{
this.params = params;
}
public Defender getDefender()
{
return defender;
}
public void setDefender(Defender defender)
{
this.defender = defender;
}
}
4)、WebXssEscapeFilterUtil工具类
/*
* 文 件 名: WebXssEscapeFilterUtil.java
*/
package com.zhgl.filter;
/**
* <一句话功能简述> <功能详细描述>
*
* @author 姓名 工号
* @version [版本号, 2015-12-2]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class WebXssEscapeFilterUtil
{
private static WebXssEscapeFilterUtil xssUtil;
private static WebXssEscapeFilterConfig config;
private WebXssEscapeFilterUtil()
{
config = new WebXssEscapeFilterConfig();
}
public static WebXssEscapeFilterUtil getInstance()
{
return xssUtil;
}
public WebXssEscapeFilterRule getFilterRule(String url,String actionName){
WebXssEscapeFilterRule urlRule = config.getUrlParamRule(url, actionName);
return urlRule;
}
/* public String doFilter(String url, String actionName, String paramName, String value)
{
WebXssEscapeFilterRule urlRule = config.getUrlParamRule(url, actionName, paramName);
if (urlRule == null)
{
return value;
}
return urlRule.getDefender().doFilter(value);
}*/
static
{
try
{
xssUtil = new WebXssEscapeFilterUtil();
}
catch (Exception e)
{
throw new ExceptionInInitializerError(e);
}
}
}
5、默认的过滤规则接口类Defender.java
/*
* 文 件 名: Defender.java
*/
package com.zhgl.filter.defender;
/**
* <一句话功能简述>
* <功能详细描述>
*
* @author 姓名 工号
* @version [版本号, 2015-12-2]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public abstract interface Defender
{
public abstract Boolean doFilter(String paramString);
}
6、规则接口的实现类DefaultDefender.java
/*
* 文 件 名: defaultDefender.java
*/
package com.zhgl.filter.defender;
import java.util.regex.Pattern;
/**
* <一句话功能简述>
* <功能详细描述>
*
* @author 姓名 工号
* @version [版本号, 2015-12-2]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class DefaultDefender implements Defender
{
private static final String XSS_REGEX = ".*[[<>]+|[']+|[\"]+|[/]+|[&]+|[\\\\]+].*";
/**
* 重载方法
* @param paramString
* @return
*/
@Override
public Boolean doFilter(String paramString)
{
Pattern pattern = Pattern.compile(XSS_REGEX);
if(!pattern.matcher(paramString).matches()){
return true;
}else{
return false;
}
}
}
7、总结:实现思路主要通过过滤器将特殊字符进行过滤掉,在过滤器第一次执行的时候加载过滤规则并将保存到缓存中。