WEB安全实战(五)XSS 攻击的另外一种解决方案(推荐)

来源:互联网 发布:网络大电影成本 编辑:程序博客网 时间:2024/06/05 16:30


说到 XSS 攻击,前边已经有两篇文章在讲这个事了,这次又拿出来说,主要是针对最近工作中的一些新的问题。那么之前是怎么解决这个问题的呢?为什么又要换解决方案?下面就详细的跟大家分享一下。

旧方案


公司的测试团队发现这个问题之后,就要求尽快的解决,在网上查了很多相关的资料,也翻阅了基本安全方面的书,基于 XSS 的攻击原理,自己写了一个 Filter,并在该 Filter 中加入了对各种请求的处理代码。首先是拦截浏览器发出的请求,然后对拦截到的请求进行过滤,获取参数列表,参数值列表(包括表单提交),然后对这些参数值进行危险字符的过滤处理,处理的过程中分为两种情况。

其中一种是把危险字符进行转换,保留原来的语意,在适当的时候进行还原,当然,这种情况下危险字符还是存在的,只不过是换了一种相对安全的形式。这种情况的好处是,保证了请求的原始性,但是对数据库或文件系统产生一些不必要的垃圾信息。为什么这么说呢,因为普通的用户在正常的操作下是不会包含这些非法字符的,只有在非法入侵的情况下才会出现这种情况,而对于危险字符的转换就是一种间接的保护措施。

对于第二种情况,不是对危险字符进行转换,而是把请求中的危险字符进行过滤,使得请求纯净化,把非法的字符过滤掉,这样以来,进入数据库或文件系统的就仅仅是合法的语句。这种情况也会有他的利弊。比如,在过滤的过程中,可能把用户的某些正常请求参数值也给清理掉,这样就不能保证请求的原始性。但系统的安全性方面却大大的提高了。为了避免用户的请求失真,我们能做的就是尽量的避开用户可以输入的特殊字符,在注册、登录、或者其他的请求中,规定用户可以使用的特殊字符,对那些不提倡使用的字符进行过滤和处理。

问题


在旧的的方案提交给测试团队之后,很大一部分的 XSS 攻击问题已经不存在了,但又出现了另外一个问题,在登录的过程中,偶尔会把验证码给过滤掉,还有一些不定的 URL 会时不时的漏掉,虽然在 web.xml 配置了全局的请求,但还是会出现这样的问题。测试团队给出的报告中显示,还有少量的 XSS 攻击存在,让我尽快解决。

针对这个问题,我又研究了问题所在区域的源代码,并结合自己写的 XSSFilter,发现了2点问题。

其一,是后台的验证逻辑并不完整,对于提交过来的请求,并没有进行相应的处理。

其二,是 XSSFilter 写的有问题,正如我上面讲到的情况一,只是把危险字符进行转换,并没有从根本上解决问题。

俗话说,久病成良医。那么,总想着一件事,问题解决起来也总会是事半功倍的。


新方案


对于出现的问题,我这里有两点需要说,其一就是针对后台的验证逻辑,这个没有必要再详细说了,无非就行完善验证逻辑,针对可能出现的情况进行抽象,抽出通用的验证逻辑,对于个别的验证就单独处理,不过还是要考虑代码的复用性。

其二,是关于 XSS 攻击全局处理的。我这里给出两种解决方案,详述如下。

方案一

如果你遇到的情况,对与用户的输入、输出有明确的限制,而且对于特殊字符也有明确的规定,那么你可以自己写一个 XSSFilter,使用上面提到的情况二,对不建议输入的特殊字符进行过滤和清理,包括 SQL 注入的一些敏感信息,都是要过滤掉的。基本的 Filter 网上都有,你要做的就是针对自己的业务,再加入对危险字符的处理即可。

方案二

想到一句老话,站在巨人的肩膀上。第二种方案,就是站在巨人的肩膀上。推荐一款开源插件,xssProject,具体作者不详。GoogleCode 中提供相应了的源代码。想研究的可以自己去找一下。下面着重讲一下,如何在项目中集成 xssProject,并使之为我们服务。

首先,项目中需要引入 xssProtect-0.1.jar、antlr-3.0.1.jar、antlr-runtime-3.0.1.jar 等3个 jar 包。

然后,封装 request,代码如下。

[java] view plain copy
  1. <span style="font-family:Comic Sans MS;">public class NewXssHttpServletRequestWrapper extends HttpServletRequestWrapper {  
  2.   
  3.     HttpServletRequest orgRequest = null;  
  4.       
  5.     public NewXssHttpServletRequestWrapper(HttpServletRequest request) {  
  6.         super(request);  
  7.         orgRequest = request;  
  8.     }  
  9.       
  10.   
  11.     /** 
  12.      * 覆盖getParameter方法,将参数名和参数值都做xss过滤。<br/> 
  13.      * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/> 
  14.      * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 
  15.      */  
  16.     @Override  
  17.     public String getParameter(String name)  
  18.     {  
  19.         System.out.println("NewXssFilter处理前的 Value = " + super.getParameterValues(name));  
  20.           
  21.         String value = super.getParameter(xssEncode(name));  
  22.         if (value != null)  
  23.         {  
  24.             value = xssEncode(value);  
  25.         }  
  26.           
  27.         System.out.println("NewXssFilter处理后的 Value = " + value);  
  28.           
  29.         return value;  
  30.     }  
  31.   
  32.     /** 
  33.      * 覆盖getHeader方法,将参数名和参数值都做xss过滤。<br/> 
  34.      * 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/> getHeaderNames 也可能需要覆盖 
  35.      */  
  36.     @Override  
  37.     public String getHeader(String name)  
  38.     {  
  39.   
  40.         String value = super.getHeader(xssEncode(name));  
  41.         if (value != null)  
  42.         {  
  43.             value = xssEncode(value);  
  44.         }  
  45.         return value;  
  46.     }  
  47.   
  48.     /** 
  49.      * 将容易引起xss漏洞的半角字符直接替换成全角字符 
  50.      *  
  51.      * @param s 
  52.      * @return 
  53.      */  
  54.     private static String xssEncode(String s)  
  55.     {  
  56.         if (s == null || s.isEmpty())  
  57.         {  
  58.             return s;  
  59.         }  
  60.           
  61.         StringReader reader = new StringReader( s );  
  62.         StringWriter writer = new StringWriter();  
  63.         try {  
  64.             HTMLParser.process( reader, writer, new XSSFilter(), true );  
  65.               
  66.             return writer.toString();  
  67.         }   
  68.         catch (NullPointerException e) {  
  69.             return s;  
  70.         }  
  71.         catch(Exception ex)  
  72.         {  
  73.             ex.printStackTrace();  
  74.         }  
  75.           
  76.         return null;  
  77.           
  78.     }  
  79.   
  80.     /** 
  81.      * 获取最原始的request 
  82.      *  
  83.      * @return 
  84.      */  
  85.     public HttpServletRequest getOrgRequest()  
  86.     {  
  87.         return orgRequest;  
  88.     }  
  89.   
  90.     /** 
  91.      * 获取最原始的request的静态方法 
  92.      *  
  93.      * @return 
  94.      */  
  95.     public static HttpServletRequest getOrgRequest(HttpServletRequest req)  
  96.     {  
  97.         if (req instanceof NewXssHttpServletRequestWrapper)  
  98.         {  
  99.             return ((NewXssHttpServletRequestWrapper) req).getOrgRequest();  
  100.         }  
  101.   
  102.         return req;  
  103.     }  
  104.   
  105. }</span>  


再然后,创建过滤器 NewXssFilter 。

[java] view plain copy
  1. <span style="font-family:Comic Sans MS;">public class NewXssFilter implements Filter {  
  2.   
  3.     FilterConfig filterConfig = null;  
  4.   
  5.     @Override  
  6.     public void destroy() {  
  7.         this.filterConfig = null;  
  8.     }  
  9.   
  10.     @Override  
  11.     public void doFilter(ServletRequest request, ServletResponse response,  
  12.             FilterChain chain) throws IOException, ServletException {  
  13.         String path = ((HttpServletRequest) request).getContextPath();  
  14.         String basePath = request.getScheme() + "://" + request.getServerName()  
  15.                 + ":" + request.getServerPort() + path + "/";  
  16.   
  17.         // HTTP 头设置 Referer过滤  
  18.         String referer = ((HttpServletRequest) request).getHeader("Referer"); // REFRESH  
  19.         if (referer != null && referer.indexOf(basePath) < 0) {  
  20.             ((HttpServletRequest) request).getRequestDispatcher(  
  21.                     ((HttpServletRequest) request).getRequestURI()).forward(  
  22.                     ((HttpServletRequest) request), response);  
  23.             System.out.println("referer不为空,referer >>>>>>>>>>>>>> " + referer);  
  24.         }  
  25.         NewXssHttpServletRequestWrapper xssRequest = new NewXssHttpServletRequestWrapper((HttpServletRequest) request);  
  26.         chain.doFilter(xssRequest, response);  
  27.     }  
  28.   
  29.     @Override  
  30.     public void init(FilterConfig filterConfig) throws ServletException {  
  31.         this.filterConfig = filterConfig;  
  32.     }  
  33.   
  34. }</span>  


最后,在 web.xml 中配置过滤器 。

[html] view plain copy
  1. <span style="font-family:Comic Sans MS;"><filter>  
  2.     <filter-name>XssSqlFilter</filter-name>  
  3.     <filter-class>com.***.web.common.NewXssFilter</filter-class>  
  4. </filter>  
  5. <filter-mapping>  
  6.     <filter-name>XssSqlFilter</filter-name>  
  7.     <url-pattern>/*</url-pattern>  
  8.     <dispatcher>REQUEST</dispatcher>  
  9. </filter-mapping></span>  


结束语


以上这些就是全部的内容了,针对 XSS 攻击的解决方案,写出来跟大家分享,个人建议使用 xssProject 来解决这一问题。毕竟 xssProject 已经提供了很完善的过滤、处理方案,你可以通过研究他的代码来进行扩展,如果需要的话。最后附上 xssProject 所需的三个 jar 包。


xssProject 下载


原创粉丝点击