这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
目标
使用 Spring Boot 的 Filter 对参数拦截,使用 Jsoup 对 参数中的 XSS进行过滤
工具
- Spring Boot 2.0
- Jsoup (可选)
实现原理
Spring Boot 的 Filter 拦截到前端的参数后进行过滤(看着是不是很简单??)。
说白了就是两个功能:参数拦截、脚本过滤。
参数拦截
想要过滤XSS首先要能拦截到前端的参数。
先写个Filter:
import java.io.IOException;
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;
public class XSSEscapeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//后面会有 XssHttpServletRequestWrapper 的代码。这个类是自己定义的
chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
}
}
复制代码
这个Filter 是可以拦截到请求的,但是呢,如果想要对参数进行修改就需要重新定义 HttpServletRequestWrapper,只有用自定义的HttpServletRequestWrapper 才能对参数进行修改。
下面定义 XssHttpServletRequestWrapper:
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* 实现XSS过滤
* Create by zdRan on 2018/5/8
*
* @author cm.zdran@gmail.com
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
private HttpServletRequest orgRequest = null;
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
@Override
public String getParameter(String name) {
// 对参数进行修改
return name;
}
@Override
public Map getParameterMap() {
// 对参数进行修改
return super.getParameterMap();;
}
@Override
public String[] getParameterValues(String name) {
String[] arr = super.getParameterValues(name);
// 对参数进行修改
return arr;
}
@Override
public String getHeader(String name) {
//对参数进行修改
return super.getHeader(name);;
}
/**
* 获取最原始的request
*
* @return
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取最原始的request的静态方法
*
* @return
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if (req instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) req).getOrgRequest();
}
return req;
}
复制代码
这样就能对参数进行修改了,但是,目前的情况还不能处理POST请求,或者 RequestBody 注解。
当使用 RequestBody 注解时,你会发现,重写的这几个方法都没有走,说明我们没有重写全方法。
找了一些资料发现:RequestBody注解读取参数的方法是getInputStream() 。
我们重写一下这个方法:
@Override
public ServletInputStream getInputStream() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(orgRequest.getInputStream()));
String line = br.readLine();
String result = "";
if (line != null) {
//对参数进行处理
}
return new WrappedServletInputStream(new ByteArrayInputStream(result.getBytes()));
}
复制代码
然后启动这个 Filter
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.DispatcherType;
/**
* Create by zdRan on 2018/5/8
*
* @author cm.zdran@gmail.com
*/
@Configuration
public class XssFilterConfiguration {
/**
* xss过滤拦截器
*/
@Bean
public FilterRegistrationBean xssFilterRegistrationBean() {
FilterRegistrationBean initXssFilterBean = new FilterRegistrationBean();
initXssFilterBean.setFilter(new XSSEscapeFilter());
initXssFilterBean.setOrder(1);
initXssFilterBean.setEnabled(true);
initXssFilterBean.addUrlPatterns("/*");
initXssFilterBean.setDispatcherTypes(DispatcherType.REQUEST);
return initXssFilterBean;
}
}
复制代码
到这里基本上就拦截到参数了,你可以自己定义对参数的修改规则。也可以使用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 标签即便是返回给前端,浏览器也无法解析,因为标签是错误的。如果你真的需要过滤这种参数。
可以尝试直接过滤特殊字符。
近期评论