目标
使用 Spring Boot 的 Filter 对参数拦截,使用 Jsoup 对 参数中的 XSS进行过滤
工具
Spring Boot 2.0
Jsoup (可选)
实现原理
Spring Boot 的 Filter 拦截到前端的参数后进行过滤(看着是不是很简单??)。
说白了就是两个功能:参数拦截、脚本过滤。
参数拦截
想要过滤XSS首先要能拦截到前端的参数。
先写个Filter:
importjava.io.IOException;importjavax.servlet.Filter;importjavax.servlet.FilterChain;importjavax.servlet.FilterConfig;importjavax.servlet.ServletException;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;publicclassXSSEscapeFilterimplementsFilter{@Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{}@Overridepublicvoiddestroy(){}@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{//后面会有XssHttpServletRequestWrapper的代码。这个类是自己定义的chain.doFilter(newXssHttpServletRequestWrapper((HttpServletRequest)request),response);}}
这个Filter 是可以拦截到请求的,但是呢,如果想要对参数进行修改就需要重新定义 HttpServletRequestWrapper,只有用自定义的HttpServletRequestWrapper 才能对参数进行修改。
下面定义 XssHttpServletRequestWrapper:
importorg.apache.commons.lang3.StringUtils;importorg.jsoup.Jsoup;importorg.jsoup.nodes.Document;importorg.jsoup.safety.Whitelist;importjavax.servlet.ReadListener;importjavax.servlet.ServletInputStream;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletRequestWrapper;importjava.io.*;importjava.util.HashMap;importjava.util.Iterator;importjava.util.Map;/***实现XSS过滤*CreatebyzdRanon2018/5/8**@authorcm.zdran@gmail.com*/publicclassXssHttpServletRequestWrapperextendsHttpServletRequestWrapper{privateHttpServletRequestorgRequest=null;publicXssHttpServletRequestWrapper(HttpServletRequestrequest){super(request);orgRequest=request;}@OverridepublicStringgetParameter(Stringname){//对参数进行修改returnname;}@OverridepublicMapgetParameterMap(){//对参数进行修改returnsuper.getParameterMap();;}@OverridepublicString[]getParameterValues(Stringname){String[]arr=super.getParameterValues(name);//对参数进行修改returnarr;}@OverridepublicStringgetHeader(Stringname){//对参数进行修改returnsuper.getHeader(name);;}/***获取最原始的request**@return*/publicHttpServletRequestgetOrgRequest(){returnorgRequest;}/***获取最原始的request的静态方法**@return*/publicstaticHttpServletRequestgetOrgRequest(HttpServletRequestreq){if(reqinstanceofXssHttpServletRequestWrapper){return((XssHttpServletRequestWrapper)req).getOrgRequest();}returnreq;}
这样就能对参数进行修改了,但是,目前的情况还不能处理POST请求,或者 RequestBody 注解。
当使用 RequestBody 注解时,你会发现,重写的这几个方法都没有走,说明我们没有重写全方法。
找了一些资料发现:RequestBody注解读取参数的方法是getInputStream() 。
我们重写一下这个方法:
@OverridepublicServletInputStreamgetInputStream()throwsIOException{BufferedReaderbr=newBufferedReader(newInputStreamReader(orgRequest.getInputStream()));Stringline=br.readLine();Stringresult="";if(line!=null){//对参数进行处理}returnnewWrappedServletInputStream(newByteArrayInputStream(result.getBytes()));}
然后启动这个 Filter
importorg.springframework.boot.web.servlet.FilterRegistrationBean;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjavax.servlet.DispatcherType;/***CreatebyzdRanon2018/5/8**@authorcm.zdran@gmail.com*/@ConfigurationpublicclassXssFilterConfiguration{/***xss过滤拦截器*/@BeanpublicFilterRegistrationBeanxssFilterRegistrationBean(){FilterRegistrationBeaninitXssFilterBean=newFilterRegistrationBean();initXssFilterBean.setFilter(newXSSEscapeFilter());initXssFilterBean.setOrder(1);initXssFilterBean.setEnabled(true);initXssFilterBean.addUrlPatterns("/*");initXssFilterBean.setDispatcherTypes(DispatcherType.REQUEST);returninitXssFilterBean;}}
到这里基本上就拦截到参数了,你可以自己定义对参数的修改规则。也可以使用jsoup对XSS进行过滤
脚本过滤
使用 jsoup 对参数中的 标签进行过滤 添加依赖
<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.11.3</version></dependency>
好了。到这就算结束了,不过目前还有一个小问题。
使用 Jsoup 是可以过滤掉所有的html标签,但是也有个问题,比如
参数是: {"name":"<html","passwd":"12345"},过滤后的结果是:{"name":"
因为没有找到<html>
标签的结束位置,所以就会过滤掉后面所有的参数。
这样就会导致 controller 获取参数的时候异常。
但是这种 html 标签即便是返回给前端,浏览器也无法解析,因为标签是错误的。如果你真的需要过滤这种参数。
可以尝试直接过滤特殊字符。