我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

springboot中使用过滤器,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工具类

一个filter的配置类

2.5进行数据测试

3.代码实现过程

3.1.jsoup依赖:


<!--screen xss --> 
<dependency> 
<groupId>org.jsoup</groupId> 
<artifactId>jsoup</artifactId> 
<version>1.9.2</version> 
</dependency>

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;  
 

 
public class JsoupUtil {   
private static final Whitelist whitelist = Whitelist.simpleText();//jsoup白名单种类,有四种,每一种针对的标签类型不一样,具体的可以ctrl+左键点击simpleText,在jsoup源码中有响应的注释和标签名单
//add myself white list label
private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false);
static {
whitelist.addAttributes(":all", "style").addTags("p").addTags("strong");//将自定义标签添加进白名单,除开白名单之外的标签都会被过滤
whitelist.preserveRelativeLinks(true);//这个配置的意思的过滤如果找不到成对的标签,就只过滤单个标签,而不用把后面所有的文本都进行过滤。
//(之前在这个问题上折腾了很久,当<script>标签只有一个时,会<script>标签后面的数据全部过滤)
}
 
public static String clean(String content) { //过滤方法
return Jsoup.clean(content, "", whitelist, outputSettings);
}
 
//test main
public static void main(String[] args) throws FileNotFoundException, IOException {
String text = "<a href=\"http://www.baidu.com/a\" onclick=\"alert(1);\"><strong><p>sss</p></strong></a><script>alert(0);</script>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;
 

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 {//get  
        BufferedReader 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()));
    }
    @Override
    public String getParameter(String name) {
        if (("content".equals(name) || name.endsWith("WithHtml"))) {
            return super.getParameter(name);
        }
        name = clean(name);
        String value = super.getParameter(name);
        if (!StringUtils.isEmpty(value)) {
            value = clean(value);
        }
        return value;
    }
 
    @Override
    public Map getParameterMap() {
        Map map = super.getParameterMap();
        // 返回值Map
        Map<String, String> returnMap = new HashMap<String, String>();
        Iterator entries = map.entrySet().iterator();
        Map.Entry entry;
        String name = "";
        String value = "";
        while (entries.hasNext()) {
            entry = (Map.Entry) entries.next();
            name = (String) entry.getKey();
            Object valueObj = entry.getValue();
            if (null == valueObj) {
                value = "";
            } else if (valueObj instanceof String[]) {
                String[] values = (String[]) valueObj;
                for (int i = 0; i < values.length; i++) {
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length() - 1);
            } else {
                value = valueObj.toString();
            }
            returnMap.put(name, clean(value).trim());
        }
        return returnMap;
    }
 
    @Override
    public 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;
    }
 
    
    public HttpServletRequest getOrgRequest() {
        return orgRequest;
    }
 
    
    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;
        }
 
        @Override
        public int read() throws IOException {
            return stream.read();
        }
 
        @Override
        public boolean isFinished() {
            return true;
        }
 
        @Override
        public boolean isReady() {
            return true;
        }
 
        @Override
        public 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;
 

//@WebFilter
//@Component   在这里可以不用这个注解,以为后面我们会在config中去配置这个filter,在这里只需要实现 Filter  接口实现相应的方法就ok
public class XssFilter implements Filter {
    private static boolean IS_INCLUDE_RICH_TEXT = false;//用于接收配置中的参数,决定这个过滤器是否开启
    public List<String> excludes = new ArrayList<String>();//用于接收配置中的参数,决定哪些是不需要过滤的url(在这里,也可以修改handleExcludeURL()方法中相应的代码,使其变更为只需要过滤的url)
    @Override
    public 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);
    }

    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;
    }

    @Override
    public 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]);
            }
        }
    }
 
    @Override
    public 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;
 

@Configuration
public class XssConfig {
    @Bean
    public FilterRegistrationBean xssFilterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new XssFilter());
        filterRegistrationBean.setOrder(1);//filter order ,set it first
        filterRegistrationBean.setEnabled(true);
        filterRegistrationBean.addUrlPatterns("/*"); //set filter all url mapping
        Map<String, String> initParameters = Maps.newHashMap();
        initParameters.put("excludes", "/oauth/token");///white list url
        initParameters.put("isIncludeRichText", "true");//enable or disable
        filterRegistrationBean.setInitParameters(initParameters);
        return filterRegistrationBean;
    }
}

调试截图:

请求:

程序截图:

运行结果:

可以看到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脚本,查数据的时候,这段脚本就会被执行,这东西后果挺严重啊,如果是在桌面外弹框,执行个挖矿脚本,这玩意不得了啊,厉害,长知识了。。。


<dependency>
 <groupId>org.jsoup</groupId>
 <artifactId>jsoup</artifactId>
 <version>1.11.3</version>
</dependency>

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

springboot中怎么使用过滤器以及jsoup过滤XSS脚本怎么写

springboot中怎么使用过滤器以及jsoup过滤XSS脚本怎么写,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。springboot使用过滤器,jsoup过滤XSS脚本背景
2023-06-22

Vue过滤器使用方法详解

过滤器的功能是对要显示的数据进行格式化后再显示,其并没有改变原本的数据,只是产生新的对应的数据,下面这篇文章主要给大家介绍了关于Vue中过滤器定义以及使用方法的相关资料,需要的朋友可以参考下
2022-12-15

一文详解Vue中过滤器filters的使用

Vue.js允许自定义过滤器,过滤器的作用可被用于一些常见的文本格式化(也就是修饰文本,但是文本内容不会改变),本文主要来和大家讲讲过滤器filters的使用,感兴趣的可以了解一下
2023-05-17

JavaWeb中过滤器Filter的用法详解

过滤器通常对一些web资源进行拦截,做完一些处理器再交给下一个过滤器处理,直到所有的过滤器处理器,再调用servlet实例的service方法进行处理。本文将通过示例为大家讲解JavaWeb中过滤器Filter的用法与实现,需要的可以参考一下
2022-11-13

JavaWeb开发中过滤器和监听器使用详解

这篇文章主要为大家详细介绍了Java中的过滤器Filter和监听器Listener的使用以及二者的区别,文中的示例代码讲解详细,需要的可以参考一下
2022-11-13

Jinja2过滤器的使用、控制语句示例详解

在Python中,如果需要对某个变量进行处理,我们可以通过函数来实现,这篇文章主要介绍了Jinja2过滤器的使用、控制语句,需要的朋友可以参考下
2023-03-02

PHP中的过滤器(Filter)是如何工作的?(请解释PHP中过滤器的使用场景和工作原理。)

PHP中的过滤器是一种验证、清理和转换数据的强大工具。它们通过过滤器链逐个应用过滤器来工作,每个过滤器执行特定操作。过滤器在各种场景下使用,包括表单验证、数据清理、安全过滤和数据转换。它们易于使用、高效且可靠,并提供了一组预定义的规则和函数,简化了数据处理过程。最佳实践包括选择适当的过滤器、使用过滤器链、检查错误、自定义过滤器和避免过度过滤。
PHP中的过滤器(Filter)是如何工作的?(请解释PHP中过滤器的使用场景和工作原理。)
2024-04-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录