防sql注入和跨脚本攻击

Wlitttle 2016-03-01

公司做的一个web项目中有这个功能,记录并学习下。在web系统中很多页面有文本输入的功能,有些不严谨的程序,通过写一些特殊字符,js片段,sql脚步会导致程序出现bug,现在通过一个统一的功能进行屏蔽。主要通过过滤器、xml解析的机制实现。

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、总结:实现思路主要通过过滤器将特殊字符进行过滤掉,在过滤器第一次执行的时候加载过滤规则并将保存到缓存中。

相关推荐

jiong / 0评论 2020-09-17