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工具类

一个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;  

/**
* @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 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;

/**
 * @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 {//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;
    }

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

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

/**
 * @Auther: qianshanmuxue
 * @Date: 2019/2/27 16:25
 * @Description:
 */
//@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);
    }
/**
*此方法是决定对当前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)
 *
 */
    @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;

/**
 * @Auther: qianshanmuxue
 * @Date: 2019/2/27 16:49
 * @Description: xss filter config
 */
@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>

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

(0)

相关推荐

  • Springboot实现XSS漏洞过滤的示例代码

    背景 前阵子做了几个项目,终于开发完毕,进入了测试阶段,信心满满将项目部署到测试环境,然后做了安全测评之后..... ​(什么!你竟然说我代码不安全???) 然后测出了 Xss漏洞 安全的问题 解决方案 场景:可以在页面输入框输入JS脚本, 攻击者可以利用此漏洞执行恶意的代码 ! 问题演示 ​ ​ 所以我们要对于前端传输的参数做处理,做统一全局过滤处理 既然要过滤处理,我们首先需要实现一个自定义过滤器 总共包含以下四部分 XssUtil XssFilterAutoConfig XssHttpSe

  • SpringBoot去除参数前后空格和XSS过滤

    去除XSS字符串需要借助工具类 jsoup ,这里jsoup有一点需要注意的是,jsoup的功能可能有点太强大了,能把xss攻击的内容直接过滤掉了不说,也会对英文尖括号<>转义,到接口里面拿到的参数就变成了<>,存库里面的就是转义后的字符串了.取出来的时候需要转一下. 比如前台传的参数传的是: 12<>3<script>alter('11111111')</script>455 过滤处理了后,到后台接口里面就成了:[12<>3455]

  • Spring Boot XSS 攻击过滤插件使用

    XSS 是什么 XSS(Cross Site Scripting)攻击全称跨站脚本攻击,为了不与 CSS(Cascading Style Sheets)名词混淆,故将跨站脚本攻击简称为 XSS,XSS 是一种常见 web 安全漏洞,它允许恶意代码植入到提供给其它用户使用的页面中. xss 攻击流程 简单 xss 攻击示例若网站某个表单没做相关的处理,用户提交相关恶意代码,浏览器会执行相关的代码. 解决方案 XSS 过滤说明 对表单绑定的字符串类型进行 xss 处理. 对 json 字符串数据进行

  • springboot2.x使用Jsoup防XSS攻击的实现

    后端应用经常接收各种信息参数,例如评论,回复等文本内容.除了一些场景下面,可以特定接受的富文本标签和属性之外(如:b,ul,li,h1, h2, h3...),需要过滤掉危险的字符和标签,防止xss攻击. 一.什么是XSS? 看完这个,应该有一个大致的概念. XSS攻击常识及常见的XSS攻击脚本汇总 XSS过滤速查表 二.准则 永远不要相信用户的输入和请求的参数(包括文字.上传等一切内容) 参考第1条 三.实现做法 结合具体业务场景,对相应内容进行过滤,这里使用Jsoup. jsoup是一款Ja

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

    目录 springboot使用过滤器,jsoup过滤XSS脚本 1.把可能包含脚本的参数位置分析一下 2.分析实现过程 3.代码实现过程 使用jsoup防止XSS攻击 springboot使用过滤器,jsoup过滤XSS脚本 背景:略 目标:完成request请求中的脚本过滤 技术:filter,jsoup,requestWapper 1.把可能包含脚本的参数位置分析一下 post/put/delete: 请求的参数中,有可能是表单提交.也有可能是使用了@requestBody注解,那么参数就是

  • SpringBoot中shiro过滤器的重写与配置详解

    目录 问题 解决方案 实现代码 1.重写shiro 登录 过滤器 2.重写role权限 过滤器 3.配置过滤器 问题 遇到问题:在前后端分离跨域访问的项目中shiro进行权限拦截失效 (即使有正确权限的访问也会被拦截) 时造成302重定向错误等问题报错:Response for preflight is invalid (redirect) 1.302原因:使用ajax访问后端项目时无法识别重定向操作 2.shiro拦截失效原因:跨域访问时有一种带预检访问的跨域,即访问时先发出一条methods

  • SpringBoot 中使用 Validation 校验参数的方法详解

    目录 1. Validation 介绍 1.1 Validation 注解 1.2 @valid 和 @validated的区别 2. SpringBoot 中使用 Validator 校验参数 2.1 依赖引入 2.2 标注校验实体类 2.3 开启参数校验 2.3.1 简单参数校验 2.3.2 JavaBean 校验 2.4 捕捉参数校验异常 项目中写逻辑时,为保证程序的健壮性,需要对各种参数进行判断,这就导致业务代码不只健壮,还十分臃肿.其实 SpringBoot 中已经提供了 Valida

  • 解决springboot中配置过滤器以及可能出现的问题

    在springboot添加过滤器有两种方式: 1.通过创建FilterRegistrationBean的方式(建议使用此种方式,统一管理,且通过注解的方式若不是本地调试,如果在filter中需要增加cookie可能会存在写不进前端情况) 2.通过注解@WebFilter的方式 通过创建FilterRegistrationBean的方式创建多个filter以及设置执行顺序: 1.创建两个实现Filter接口的类TestFilter1 .TestFilter2 package com.aoxun.c

  • SpringBoot面试突击之过滤器和拦截器区别详解

    目录 实现过滤器和拦截器 a) 实现过滤器 b) 实现拦截器 过滤器 VS 拦截器 1.出身不同 2.触发时机不同 3.实现不同 4.支持的项目类型不同 5.使用的场景不同 总结 实现过滤器和拦截器 首先,我们先来看一下二者在 Spring Boot 项目中的具体实现,这对后续理解二者的区别有很大的帮助. a) 实现过滤器 过滤器可以使用 Servlet 3.0 提供的 @WebFilter 注解,配置过滤的 URL 规则,然后再实现 Filter 接口,重写接口中的 doFilter 方法,具

  • springboot使用logback文件查看错误日志过程详解

    这篇文章主要介绍了springboot使用logback文件查看错误日志过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 <?xml version="1.0" encoding="UTF-8"?> <!-- 从高到地低 OFF . FATAL . ERROR . WARN . INFO . DEBUG . TRACE . ALL --> <!-- 日志输出规则 根据当前ROOT

  • SpringBoot使用Shiro实现动态加载权限详解流程

    目录 一.序章 二.SpringBoot集成Shiro 1.引入相关maven依赖 2.自定义Realm 3.Shiro配置类 三.shiro动态加载权限处理方法 四.shiro中自定义角色与权限过滤器 1.自定义uri权限过滤器 zqPerms 2.自定义角色权限过滤器 zqRoles 3.自定义token过滤器 五.项目中会用到的一些工具类常量等 1.Shiro工具类 2.Redis常量类 3.Spring上下文工具类 六.案例demo源码 一.序章 基本环境 spring-boot 2.1

  • 基于JavaScript表单脚本(详解)

    什么是表单? 一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法. 表单域:包含了文本框.密码框.隐藏域.多行文本框.复选框.单选框.下拉选择框和文件上传框等. 表单按钮:包括提交按钮.复位按钮和一般按钮:用于将数据传送到服务器上的CGI脚本或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作. JavaScript与表单间的关系:JS最初的应用就是用于分担服务器处理表单的责任,打破依赖服务器的局面,尽管目前web和jav

  • PHP中filter函数校验数据的方法详解

    介绍PHP中filter函数校验数据的方法详解,PHP过滤器包含两种类型:Validation用来验证验证项是否合法 .Sanitization用来格式化被验证的项目,因此它可能会修改验证项的值,将不合法的字符删除. input_filters_list() 用来列出当前系统所支持的所有过滤器. 复制代码 代码如下: <?php foreach(filter_list() as $id => $filter) {     echo $filter.' '.filter_id($filter).

  • JAVAEE Filter 过滤器设置是否缓存实例详解

    在网页中,每次的客户端访问服务器,有部分不用重复请求,如有些图片,视频等就没有必要每次都请求,这样会让服务器增大工作量.为了防止这样,我们采用过滤器来设置客户端是都缓存. 页面的缓存与不缓存设置及html页面中meta的作用 HTTP1.1中启用Cache-Control 来控制页面的缓存与否,这里介绍几个常用的参数: no-cache,浏览器和缓存服务器都不应该缓存页面信息: public,浏览器和缓存服务器都可以缓存页面信息: no-store,请求和响应的信息都不应该被存储在对方的磁盘系统

随机推荐