SpringBoot2.x 整合 AntiSamy防御XSS攻击的简单总结

AntiSamy是OWASP的一个开源项目,通过对用户输入的HTML、CSS、JavaScript等内容进行检验和清理,确保输入符合应用规范。AntiSamy被广泛应用于Web服务对存储型和反射型XSS的防御中。

XSS攻击全称为跨站脚本攻击(Cross Site Scripting),是一种在web应用中的计算机安全漏洞,它允许用户将恶意代码(如script脚本)植入到Web页面中,为了不和层叠样式表(Cascading Style Sheets, CSS)混淆,一般缩写为XSS。XSS分为以下两种类型:

  • 存储型XSS:服务端对用户输入的恶意脚本没有经过验证就存入数据库,每次调用数据库都会将其渲染在浏览器上。则可能为存储型XSS。
  • 反射型XSS:通过get或者post等方式,向服务端输入数据。如果服务端不进行过滤,验证或编码,直接将用户信息呈现出来,可能会造成反射型XSS。

本文主要对SpringBoot2.x集成AntiSamy防御XSS攻击进行简单总结,其中SpringBoot使用的2.4.5版本。

一、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- AntiSamy依赖 -->
<dependency>
    <groupId>org.owasp.antisamy</groupId>
    <artifactId>antisamy</artifactId>
    <version>1.6.2</version>
</dependency>
<!-- lombok插件 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.9</version>
</dependency>

二、策略文件

Antisamy对恶意代码的过滤依赖于策略文件,策略文件为xml格式,规定了AntiSamy对各个标签、属性的处理方法。策略文件定义的严格与否,决定了AntiSamy对Xss的防御效果。在AntiSamy的jar包中,已经包含了几个常用的策略文件:

本文使用antisamy-ebay.xml作为策略文件,该策略相对安全,适用于电商网站。将antisamy-ebay.xmlantisamy.xsd复制到resouces目录下。对于策略文件的具体内容这里不进行深入了解,只需了解下对标签的处理规则<tag-rules>,共有remove、truncate、validate三种处理方式,其中remove为直接删除,truncate为缩短标签,只保留标签和值,validate为验证标签属性:

上图截取了<tag-rules>的一部分,可知对script标签的处理策略是remove。

三、实体类和Controller

用户实体类:

package com.rtxtitanv.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.model.User
 * @description 用户实体类
 * @date 2021/8/23 14:54
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
    private Long id;
    private String username;
    private String password;
}

Controller:

package com.rtxtitanv.controller;

import com.rtxtitanv.model.User;
import org.springframework.web.bind.annotation.*;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.UserController
 * @description UserController
 * @date 2021/8/23 14:54
 */
@RequestMapping("/user")
@RestController
public class UserController {

    @PostMapping("/save")
    public User saveUser(User user) {
        return user;
    }

    @GetMapping("/get")
    public User getUserById(@RequestParam(value = "id") Long id) {
        return new User(id, "ZhaoYun", "123456");
    }

    @PutMapping("/update")
    public User updateUser(@RequestBody User user) {
        return user;
    }
}

四、创建过滤器

package com.rtxtitanv.filter;

import com.rtxtitanv.wrapper.XssRequestWrapper;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.filter.XssFilter
 * @description XSS过滤器
 * @date 2021/8/23 15:01
 */
public class XssFilter implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        // 拦截请求,处理XSS过滤
        chain.doFilter(new XssRequestWrapper((HttpServletRequest)request), response);
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

注意:在过滤器中并没有直接对请求参数进行过滤清洗,而是在XssRequestWrapper类中进行的。XssRequestWrapper类将当前的request对象进行了包装,在过滤器放行时会自动调用XssRequestWrapper中的方法对请求参数进行清洗。

五、创建XssRequestWrapper类

package com.rtxtitanv.wrapper;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.owasp.validator.html.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import java.util.Objects;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.wrapper.XssRequestWrapper
 * @description 装饰器模式加强对request的处理,基于AntiSamy进行XSS防御
 * @date 2021/8/23 15:01
 */
public class XssRequestWrapper extends HttpServletRequestWrapper {

    private static final Logger LOGGER = LoggerFactory.getLogger(XssRequestWrapper.class);
    private static Policy policy = null;

    static {
        try {
            // 获取策略文件路径,策略文件需要放到项目的classpath下
            String antiSamyPath = Objects
                .requireNonNull(XssRequestWrapper.class.getClassLoader().getResource("antisamy-ebay.xml")).getFile();
            LOGGER.info(antiSamyPath);
            // 获取的文件路径中有空格时,空格会被替换为%20,在new一个File对象时会出现找不到路径的错误
            // 对路径进行解码以解决该问题
            antiSamyPath = URLDecoder.decode(antiSamyPath, "utf-8");
            LOGGER.info(antiSamyPath);
            // 指定策略文件
            policy = Policy.getInstance(antiSamyPath);
        } catch (UnsupportedEncodingException | PolicyException e) {
            e.printStackTrace();
        }
    }

    public XssRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    /**
     * 过滤请求头
     *
     * @param name 参数名
     * @return 参数值
     */
    @Override
    public String getHeader(String name) {
        String header = super.getHeader(name);
        // 如果Header为空,则直接返回,否则进行清洗
        return StringUtils.isBlank(header) ? header : xssClean(header);
    }

    /**
     * 过滤请求参数
     *
     * @param name 参数名
     * @return 参数值
     */
    @Override
    public String getParameter(String name) {
        String parameter = super.getParameter(name);
        // 如果Parameter为空,则直接返回,否则进行清洗
        return StringUtils.isBlank(parameter) ? parameter : xssClean(parameter);
    }

    /**
     * 过滤请求参数(一个参数可以有多个值)
     *
     * @param name 参数名
     * @return 参数值数组
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] parameterValues = super.getParameterValues(name);
        if (parameterValues != null) {
            int length = parameterValues.length;
            String[] newParameterValues = new String[length];
            for (int i = 0; i < length; i++) {
                LOGGER.info("AntiSamy清理之前的参数值:" + parameterValues[i]);
                // 清洗参数
                newParameterValues[i] = xssClean(parameterValues[i]);
                LOGGER.info("AntiSamy清理之后的参数值:" + newParameterValues[i]);
            }
            return newParameterValues;
        }
        return super.getParameterValues(name);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> requestMap = super.getParameterMap();
        requestMap.forEach((key, value) -> {
            for (int i = 0; i < value.length; i++) {
                LOGGER.info(value[i]);
                value[i] = xssClean(value[i]);
                LOGGER.info(value[i]);
            }
        });
        return requestMap;
    }

    /**
     * 使用AntiSamy清洗数据
     *
     * @param value 需要清洗的数据
     * @return 清洗后的数据
     */
    private String xssClean(String value) {
        try {
            AntiSamy antiSamy = new AntiSamy();
            // 使用AntiSamy清洗数据
            final CleanResults cleanResults = antiSamy.scan(value, policy);
            // 获得安全的HTML输出
            value = cleanResults.getCleanHTML();
            // 对转义的HTML特殊字符(<、>、"等)进行反转义,因为AntiSamy调用scan方法时会将特殊字符转义
            return StringEscapeUtils.unescapeHtml4(value);
        } catch (ScanException | PolicyException e) {
            e.printStackTrace();
        }
        return value;
    }

    /**
     * 通过修改Json序列化的方式来完成Json格式的XSS过滤
     */
    public static class XssStringJsonSerializer extends JsonSerializer<String> {

        @Override
        public Class<String> handledType() {
            return String.class;
        }

        @Override
        public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            if (!StringUtils.isBlank(value)) {
                try {
                    AntiSamy antiSamy = new AntiSamy();
                    final CleanResults cleanResults = antiSamy.scan(value, XssRequestWrapper.policy);
                    gen.writeString(StringEscapeUtils.unescapeHtml4(cleanResults.getCleanHTML()));
                } catch (ScanException | PolicyException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

六、创建配置类

package com.rtxtitanv.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.rtxtitanv.filter.XssFilter;
import com.rtxtitanv.wrapper.XssRequestWrapper;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import javax.servlet.Filter;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.config.AntiSamyConfig
 * @description AntiSamy配置类
 * @date 2021/8/23 15:05
 */
@Configuration
public class AntiSamyConfig {

    /**
     * 配置XSS过滤器
     *
     * @return FilterRegistrationBean
     */
    @Bean
    public FilterRegistrationBean<Filter> filterRegistrationBean() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(new XssFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setOrder(1);
        return filterRegistrationBean;
    }

    /**
     * 用于过滤Json类型数据的解析器
     *
     * @param builder Jackson2ObjectMapperBuilder
     * @return ObjectMapper
     */
    @Bean
    public ObjectMapper xssObjectMapper(Jackson2ObjectMapperBuilder builder) {
        // 创建解析器
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        // 注册解析器
        SimpleModule simpleModule = new SimpleModule("XssStringJsonSerializer");
        simpleModule.addSerializer(new XssRequestWrapper.XssStringJsonSerializer());
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}

七、测试

启动项目,发送如下POST请求,请求地址为http://localhost:8080/user/save,可见表单参数中的<script>标签内容被成功过滤:

发送如下GET请求,请求地址为http://localhost:8080/user/get?id=1<script>alert("XSS");</script>0,可见Query参数中的<script>标签内容被成功过滤:

发送如下PUT请求,请求地址为http://localhost:8080/user/update,可见Json类型参数中的<script>标签内容被成功过滤:

代码示例

Github:https://github.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-antisamy

Gitee:https://gitee.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-antisamy

到此这篇关于SpringBoot2.x 整合 AntiSamy防御XSS攻击的简单总结的文章就介绍到这了,更多相关SpringBoot2.x防御XSS攻击内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot如何防止XSS注入攻击详解

    什么是 XSS 攻击 在跨站脚本(XSS)攻击中,攻击者可以在受害者的浏览器中执行恶意脚本.这种攻击通常是通过在网页中插入恶意代码 (JavaScript) 来完成的.攻击者在使用攻击后一般能够: 修改网页内容 将用户重定向到其他网站 访问用户的 Cookie 并利用此信息来冒充用户 访问有关用户系统的关键信息,例如地理位置,网络摄像头,文件系统 将木马功能注入应用程序 如果被攻击的用户在应用程序中具有更高的权限.攻击者可以完全控制应用程序,并破坏所有用户及其数据. XSS 攻击类型 常见的 X

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

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

  • springboot项目如何防止XSS攻击

    目录 1. 什么是XSS攻击? 2. 如何防范? 2.1 什么时候注入请求参数 3. 具体处理细节 1. 什么是XSS攻击? XSS攻击全称跨站脚本攻击,是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中.也就是作恶的用户通过表单提交一些前端代码,如果不做处理的话,这些前端代码将会在展示的时候被浏览器执行. 2. 如何防范? 有两种方式,一种是一些特殊字符转义,另一种是去除一些危险html元素.本文通过JSOUP库实现第二种方式. 现在问题是,在什么

  • SpringBoot2.x 整合 AntiSamy防御XSS攻击的简单总结

    AntiSamy是OWASP的一个开源项目,通过对用户输入的HTML.CSS.JavaScript等内容进行检验和清理,确保输入符合应用规范.AntiSamy被广泛应用于Web服务对存储型和反射型XSS的防御中. XSS攻击全称为跨站脚本攻击(Cross Site Scripting),是一种在web应用中的计算机安全漏洞,它允许用户将恶意代码(如script脚本)植入到Web页面中,为了不和层叠样式表(Cascading Style Sheets, CSS)混淆,一般缩写为XSS.XSS分为以

  • 一次VUE项目中遇到XSS攻击的实战记录

    目录 前言 发现原因 自定义过滤规则 总结 前言 随着互联网的高速发展,信息安全问题已经成为企业最为关注的焦点之一,而前端又是引发企业安全问题的高危据点.在移动互联网时代,前端人员除了传统的 XSS.CSRF 等安全问题之外,又时常遭遇网络劫持.非法调用 Hybrid API 等新型安全问题.当然,浏览器自身也在不断在进化和发展,不断引入 CSP.Same-Site Cookies 等新技术来增强安全性,但是仍存在很多潜在的威胁,这需要前端技术人员不断进行"查漏补缺". 发现原因 一切

  • Spring Boot项目抵御XSS攻击实战过程

    目录 前言 一.什么是XSS攻击 二.如何抵御XSS攻击 三.实现抵御XSS攻击 总结 前言 作为Web网站来说,抵御XSS攻击是必须要做的事情,这是非常常见的黑客攻击手段之一. 一.什么是XSS攻击 XSS意思是跨站脚本攻击,英文全称Cross Site Scripting,为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS. XSS攻击的手段很简单,就是通过各种手段向我们的Web网站植入JS代码.比如说普通网站的用户注册.论坛

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

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

  • Web安全之XSS攻击与防御小结

    Web安全之XSS攻防 1. XSS的定义 跨站脚本攻击(Cross Site Scripting),缩写为XSS.恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的. 2. XSS的原理 攻击者对含有漏洞的服务器发起XSS攻击(注入JS代码). 诱使受害者打开受到攻击的服务器URL. 受害者在Web浏览器中打开URL,恶意脚本执行. 3. XSS的攻击方式 (1)反射型: 发出请求时,XSS代码出现在U

  • 浅谈Springboot2.0防止XSS攻击的几种方式

    目录 防止XSS攻击,一般有两种做法: 转义 做法的三种实现: 转义方法一:注册自定义转换器 转义方法二:BaseController 转义方法三:Converter 在平时做项目代码开发的时候,很容易忽视XSS攻击的防护,网上有很多自定义全局拦截器来实现XSS过滤,其实不需要这么麻烦,SpringBoot留有不少钩子(扩展点),据此我们可以巧妙地实现全局的XSS过滤 防止XSS攻击,一般有两种做法: 转义使用工具类HtmlUtils实现 过滤将敏感标签去除jsoup实现了非常强大的clean敏

  • .net core xss攻击防御的方法

    XSS攻击全称跨站脚本攻击 ,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中. 比如我们在表单提交的时候插入脚本代码 如果不进行处理,那么就是这种效果,我这里只是演示一个简单的弹窗 下面给大家分享一下我的解决方案. 需要用到这个库:HtmlSanitizer https://github.com/mganss/HtmlSan

  • Java防止xss攻击附相关文件下载

    跨站脚本(英语:Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种.它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响.这类攻击通常包含了HTML以及用户端脚本语言. XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序.这些恶意网页程序通常是JavaScript,但实际上也可以包括Java,VBScript,ActiveX,Flash或者甚至

  • java 最新Xss攻击与防护(全方位360°详解)

    前沿 XSS防范属于前端还是后端的责任 ? XSS 防范是后端 RD(研发人员)的责任,后端 RD 应该在所有用户提交数据的接口,对敏感字符进行转义,才能进行下一步操作. 所有要插入到页面上的数据,都要通过一个敏感字符过滤函数的转义,过滤掉通用的敏感字符后,就可以插入到页面中. 公司的搜索页面如果你是下面的写法.那么他可能存在Xss注入 <input type="text" value="<%= getParameter("keyword")

随机推荐