springboot中使用过滤器|springboot中使用过滤器,jsoup过滤XSS脚本详解

目录

  • springboot使用过滤器,jsoup过滤XSS脚本
    • 1.把可能包含脚本的参数位置分析一下
    • 2.分析实现过程
    • 3.代码实现过程
  • 使用jsoup防止XSS攻击

    springboot使用过滤器,jsoup过滤XSS脚本
    • 背景:略
    • 目标:完成request请求中的脚本过滤
    • 技术:filter,jsoup,requestWapper

    1.把可能包含脚本的参数位置分析一下
    • post/put/delete: 请求的参数中,有可能是表单提交、也有可能是使用了@requestBody注解,那么参数就是json格式,位于request的流中。
    • get/options等:可能存在于url参数中,也有可能是表单提交的预请求中,所以一般在能想到的位置都有可能存在,包括header中。

    2.分析实现过程
    2.1首先要从request请求中将各个需要过滤位置的参数取出来
    2.2然后将参数取出来进行过滤
    2.3将过滤后的参数重新包装成request传递下去
    2.4在这期间,
    • 需要准备好jsoup过滤脚本的工具类;
    • 需要自定义一个过滤器,并且在过滤器中添加匹配条件,如:那些url不需要过滤,那些请求方式必须进行过滤;
    • 对过滤器进行配置,是否开启,设置在整个过滤器链中的位置,设置过滤的白名单或者黑名单
    • 所以就很清晰了我们过滤需要哪些类,哪些配置了
    一个filter
    一个requestWapper
    一个jsoup工具类
    【springboot中使用过滤器|springboot中使用过滤器,jsoup过滤XSS脚本详解】一个filter的配置类
    2.5进行数据测试

    3.代码实现过程
    3.1.jsoup依赖:
    org.jsoup jsoup 1.9.2

    3.2jsoup工具类:JsoupUtil
    import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.safety.Whitelist; import java.io.FileNotFoundException; import java.io.IOException; /*** @Auther: qianshanmuxue* @Date: 2019/2/27 19:32* @Description: xss Illegal label filtering*/ public class JsoupUtil {private static final Whitelist whitelist = Whitelist.simpleText(); //jsoup白名单种类,有四种,每一种针对的标签类型不一样,具体的可以ctrl+左键点击simpleText,在jsoup源码中有响应的注释和标签名单//add myself white list labelprivate static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false); static {whitelist.addAttributes(":all", "style").addTags("p").addTags("strong"); //将自定义标签添加进白名单,除开白名单之外的标签都会被过滤whitelist.preserveRelativeLinks(true); //这个配置的意思的过滤如果找不到成对的标签,就只过滤单个标签,而不用把后面所有的文本都进行过滤。//(之前在这个问题上折腾了很久,当sss"; System.out.println(clean(text)); }}

    3.3request包装类XssHttpServletRequestWrapper
    import java.io.*; import java.util.*; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import com.xxx.utils.JsoupUtil; import org.jsoup.nodes.Document; import org.springframework.util.StringUtils; /** * @Auther: qianshanmuxue * @Date: 2019/2/27 16:24 * @Description:request wapper use to get request parameter and request bdoy data and wapper another request */public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { //因为我们需要获取request中的数据,所以需要继承java底层中HttpServletRequestWrapper这个类,重写父类中的某些方法,获取相应位置的参数private HttpServletRequest orgRequest = null; private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false); public XssHttpServletRequestWrapper(HttpServletRequest request) {super(request); orgRequest = request; } @Override public ServletInputStream getInputStream() throws IOException {//getBufferedReader br = new BufferedReader(new InputStreamReader(orgRequest.getInputStream())); String line = br.readLine(); String result = ""; if (line != null) {result += clean(line); } return new WrappedServletInputStream(new ByteArrayInputStream(result.getBytes())); }@Overridepublic String getParameter(String name) {if (("content".equals(name) || name.endsWith("WithHtml"))) {return super.getParameter(name); }name = clean(name); String value = https://www.it610.com/article/super.getParameter(name); if (!StringUtils.isEmpty(value)) {value = clean(value); }return value; } @Overridepublic Map getParameterMap() {Map map = super.getParameterMap(); // 返回值MapMap returnMap = new HashMap(); Iterator entries = map.entrySet().iterator(); Map.Entry entry; String name =""; String valuehttps://www.it610.com/article/= ""; while (entries.hasNext()) {entry = (Map.Entry) entries.next(); name = (String) entry.getKey(); Object valueObj = entry.getValue(); if (null == valueObj) {valuehttps://www.it610.com/article/= ""; } else if (valueObj instanceof String[]) {String[] values = (String[]) valueObj; for (int i = 0; i < values.length; i++) {value = https://www.it610.com/article/values[i] +","; }value = https://www.it610.com/article/value.substring(0, value.length() - 1); } else {value = valueObj.toString(); }returnMap.put(name, clean(value).trim()); }return returnMap; } @Overridepublic String[] getParameterValues(String name) {String[] arr = super.getParameterValues(name); if (arr != null) {for (int i = 0; i < arr.length; i++) {arr[i] = clean(arr[i]); }}return arr; } /*** get org request** @return*/public HttpServletRequest getOrgRequest() {return orgRequest; } /*** wapper request*/public static HttpServletRequest getOrgRequest(HttpServletRequest req) {if (req instanceof XssHttpServletRequestWrapper) {return ((XssHttpServletRequestWrapper) req).getOrgRequest(); }return req; } public String clean(String content) {String result = JsoupUtil.clean(content); return result; } private class WrappedServletInputStream extends ServletInputStream {public void setStream(InputStream stream) {this.stream = stream; } private InputStream stream; public WrappedServletInputStream(InputStream stream) {this.stream = stream; } @Overridepublic int read() throws IOException {return stream.read(); } @Overridepublic boolean isFinished() {return true; } @Overridepublic boolean isReady() {return true; } @Overridepublic void setReadListener(ReadListener readListener) { }}}

    3.4filter-XssFilter
    import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; 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; /** * @Auther: qianshanmuxue * @Date: 2019/2/27 16:25 * @Description: *///@WebFilter//@Component在这里可以不用这个注解,以为后面我们会在config中去配置这个filter,在这里只需要实现 Filter接口实现相应的方法就okpublic class XssFilter implements Filter {private static boolean IS_INCLUDE_RICH_TEXT = false; //用于接收配置中的参数,决定这个过滤器是否开启public List excludes = new ArrayList(); //用于接收配置中的参数,决定哪些是不需要过滤的url(在这里,也可以修改handleExcludeURL()方法中相应的代码,使其变更为只需要过滤的url)@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if (handleExcludeURL(req, resp)) {chain.doFilter(request, response); return; }XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); chain.doFilter(xssRequest, response); }/***此方法是决定对当前url是否执行过滤,*在这里没有使用请求方法(post/put)来匹配,因为在本项目中使用url匹配更适合(因为get和其他请求方式也需要进行过滤),如果你有兴趣可以把这个方法更改为匹配请求方法进行过滤**/private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {if ((excludes == null || excludes.isEmpty())&&IS_INCLUDE_RICH_TEXT) {return false; }String url = request.getServletPath(); for (String pattern : excludes) {Pattern p = Pattern.compile("^" + pattern); Matcher m = p.matcher(url); if (m.find()) {return true; }}return false; }/** *过滤器初始化,从配置类中获取参数,用于初始化两个参数(是否开启,排除指定的url list) * */@Overridepublic void init(FilterConfig arg0) throws ServletException {String isIncludeRichText = arg0.getInitParameter("isIncludeRichText"); if (StringUtils.isNotBlank(isIncludeRichText)) {IS_INCLUDE_RICH_TEXT = BooleanUtils.toBoolean(isIncludeRichText); } String temp = arg0.getInitParameter("excludes"); if (temp != null) {String[] url = temp.split(","); for (int i = 0; url != null && i < url.length; i++) {excludes.add(url[i]); }}} @Overridepublic void destroy() {}}

    3.5filter的配置类:XssConfig
    import com.xxx.filter.XssFilter; import com.google.common.collect.Maps; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Map; /** * @Auther: qianshanmuxue * @Date: 2019/2/27 16:49 * @Description: xss filter config */@Configurationpublic class XssConfig {@Beanpublic FilterRegistrationBean xssFilterRegistrationBean() {FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new XssFilter()); filterRegistrationBean.setOrder(1); //filter order ,set it firstfilterRegistrationBean.setEnabled(true); filterRegistrationBean.addUrlPatterns("/*"); //set filter all url mappingMap initParameters = Maps.newHashMap(); initParameters.put("excludes", "/oauth/token"); ///white list urlinitParameters.put("isIncludeRichText", "true"); //enable or disablefilterRegistrationBean.setInitParameters(initParameters); return filterRegistrationBean; }}

    调试截图:
    请求:
    springboot中使用过滤器|springboot中使用过滤器,jsoup过滤XSS脚本详解
    文章图片

    程序截图:
    springboot中使用过滤器|springboot中使用过滤器,jsoup过滤XSS脚本详解
    文章图片

    运行结果:
    springboot中使用过滤器|springboot中使用过滤器,jsoup过滤XSS脚本详解
    文章图片

    可以看到body中 的脚本已经被过滤了,
    然后其他的截图我就不发了,还有一种思路就是在过滤器中把字符转义。
    感谢luckpet大佬的提示
    1 BufferedReader 使用完需要关闭 ;
    2 对于一些拿postman等工具的朋友,拼接json的话会有换行 这里result += clean(line); 需要改成: while((line = br.readLine()) != null){ if (line != null) { result += line; } }

    使用jsoup防止XSS攻击 前阵子项目国测后,打开一个项目页面,莫名其妙弹出xss,搜了全局也没找到alert("xss"),问了一下项目经理,原来是国测做防注入的时候,在添加数据的时候做的,一脸懵逼。
    查了一下资料,以前做项目的时候都没想到这个问题,如果保存一段script脚本,查数据的时候,这段脚本就会被执行,这东西后果挺严重啊,如果是在桌面外弹框,执行个挖矿脚本,这玩意不得了啊,厉害,长知识了。。。
    org.jsoup jsoup 1.11.3

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

      推荐阅读