之前由我负责维护的一个项目被检测出存在可能被XSS攻击的漏洞。
吓得我赶紧恶补了下XSS。
XSS,全称为Cross Site Script,跨站脚本攻击,是WEB程序中一种常见的漏洞。其主要的攻击手段是在在利用网站上的可由用户输入信息的地方,恶意注入含有攻击性的脚本,达到攻击网站或者窃取用户cookied等隐私信息的目的。
XSS漏洞主要分为两种类型,一种是Stored XSS, 另一种是反射型 XSS。
第一种举个简单的例子就是BBS网站,黑客可以利用留言的功能,发表以下内容:
<script type="text/javascript"> alert("surprise") </script>
对系统而言,它认为这和其他的留言一样都只是字符串。但是当有用户访问到这个页面时,浏览器会把这段留言当成html内容来解析。因此就执行了其中的js脚本。
而反射型 XSS则是利用点击链接或是提交一些内容来达到攻击的目的。
比如我有以下一个页面:
<form name="formsearch" method="get"> <div class="form"> <input name="q" type="text" class="search-keyword" id="search-keyword" /> <button type="submit" class="search-submit blue-button">搜索</button> </div> </form>
当用户在input框中输入: "<script type="text/javascript"> alert(“surperise”) " 时。他也向浏览器插入了自己的JS脚本。
当表单被提交时,输入的字符串被作为参数放在了URL中,传到下一个页面。
当下一个页面需要显示这些内容,字符串中包含的攻击脚本也就被浏览器解释了出来。
当然这段脚本只能在自己的浏览器执行,可能没有什么攻击性,但是黑客们会想尽办法伪装页面达到攻击的目的。
总之,XSS攻击就是想办法让你的浏览器去执行他的脚本。
那么XSS攻击该如何方法呢。
既然是通过输入字符串达到植入脚本的目的,那么我们只要对用户输入的字符串进行一个处理就好了。
我们可以利用org.apache.commons.lang3.StringEscapeUtils(注:3.6版本起,用commons-text包下的StringEscapeUtils代替)这个类对输入的参数进行html转义。
可以定义一个过滤器,然后转义HttpServletRequest中的参数值。
代码如下:
定义拦截器
1 import java.io.IOException; 2 3 import javax.servlet.Filter; 4 import javax.servlet.FilterChain; 5 import javax.servlet.FilterConfig; 6 import javax.servlet.ServletException; 7 import javax.servlet.ServletRequest; 8 import javax.servlet.ServletResponse; 9 import javax.servlet.http.HttpServletRequest; 10 11 public class XSSFilter implements Filter { 12 @Override 13 public void init(FilterConfig filterConfig) throws ServletException { 14 15 } 16 17 @Override 18 public void doFilter(ServletRequest request, ServletResponse response, 19 FilterChain chain) throws IOException, ServletException { 20 chain.doFilter(new XSSHttpServletRequestWrapper((HttpServletRequest) request), response); 21 } 22 23 @Override 24 public void destroy() { 25 26 } 27 28 29 }
由于原生的HttpServletRequest不支持你直接修改参数,因此我们定义了一个包装类,在获取参数的时候对参数进行转义。
1 package com.jspxcms.core.support; 2 3 import javax.servlet.http.HttpServletRequest; 4 import javax.servlet.http.HttpServletRequestWrapper; 5 6 import org.apache.commons.lang3.StringEscapeUtils; 7 8 public class XSSHttpServletRequestWrapper extends HttpServletRequestWrapper { 9 10 public XSSHttpServletRequestWrapper(HttpServletRequest request) { 11 super(request); 12 } 13 14 @Override 15 public String getHeader(String name) { 16 return StringEscapeUtils.escapeHtml4(super.getHeader(name)); 17 } 18 19 @Override 20 public String getQueryString() { 21 return StringEscapeUtils.escapeHtml4(super.getQueryString()); 22 } 23 24 @Override 25 public String getParameter(String name) { 26 return StringEscapeUtils.escapeHtml4(super.getParameter(name)); 27 } 28 29 @Override 30 public String[] getParameterValues(String name) { 31 String[] values = super.getParameterValues(name); 32 if(values != null) { 33 int length = values.length; 34 String[] escapseValues = new String[length]; 35 for(int i = 0; i < length; i++){ 36 escapseValues[i] = "1"; 37 } 38 return escapseValues; 39 } 40 return super.getParameterValues(name); 41 } 42 }
最后我们在web.xml中配置我们的拦截器即可(将XXXX换成类的全限定名)。
1 <filter> 2 <filter-name>XssEscape</filter-name> 3 <filter-class>XXXXX.XSSFilter</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>XssEscape</filter-name> 7 <url-pattern>/*</url-pattern> 8 <dispatcher>REQUEST</dispatcher> 9 </filter-mapping>
简单的XSS防御就完成了。