[024][Web模块]基于 AntiSamy 的 Spring Boot XSS 防护实践:从过滤器到反序列化的多层防御

简介: 本文介绍基于OWASP AntiSamy的Spring Boot XSS多层防护方案:通过Servlet过滤器清洗表单/查询参数,结合Jackson反序列化器净化JSON数据,实现非侵入、全覆盖的输入清洗,业务代码零修改。(239字)

[024][Web模块]基于 AntiSamy 的 Spring Boot XSS 防护实践:从过滤器到反序列化的多层防御

本项目代码:https://gitee.com/yunjiao-source/tutorials4j

摘要

跨站脚本攻击(XSS)一直是 Web 应用最常见的安全漏洞之一。本文基于一个真实的企业级 Java 项目,深入分析了一套完整的 XSS 防护方案。该方案使用 OWASP AntiSamy 作为核心清洗引擎,通过 Servlet 过滤器Jackson 反序列化器 两层机制,分别拦截请求参数与 JSON 正文中的恶意代码,实现了对用户输入的全方位防护。文章将详细剖析 XssHttpServletFilterXssJsonDeserializerXssSimpleModule 以及 XssUtils 四个核心组件的设计与实现,并给出在 Spring Boot 中的集成示例。

1. 引言

XSS 攻击的本质是攻击者将恶意脚本注入到看似可信的网站中,当其他用户浏览时,脚本会在其浏览器中执行。传统的防御手段包括输出转义、输入校验、内容安全策略(CSP)等。其中,输入清洗(即过滤掉 HTML/JS 中的危险内容)是一种广谱且有效的防御手段。

本文介绍的方案基于 OWASP 的 AntiSamy 项目——一个专门用于过滤用户提交 HTML/CSS 内容的 Java 库。开发者通过提供一个策略文件(XML),精细控制哪些标签、属性、CSS 属性是允许的。与简单的正则替换相比,AntiSamy 能正确解析 HTML 结构,避免绕过攻击。

2. 整体架构

下图展示了 XSS 防护的整体数据流:

HTTP Request (GET/POST)
       │
       ▼
┌──────────────────────┐
│ XssHttpServletFilter │  → 包装 HttpServletRequest
└──────────────────────┘
       │
       ▼ (表单参数、Query参数、请求头)
┌──────────────────────────────┐
│ XssHttpServletRequestWrapper │  → 重写 getParameter 等方法
└──────────────────────────────┘
       │
       ▼
       XssUtils.cleaning()  ← AntiSamy 清洗
       │
       ▼
Controller 接收到的参数已清洗

────────────────────────────

HTTP Request (JSON Body)
       │
       ▼
┌─────────────────────┐
│ XssSimpleModule     │  → 注册到 Jackson ObjectMapper
└─────────────────────┘
       │
       ▼
┌─────────────────────────┐
│ XssJsonDeserializer     │  → 反序列化 String 字段时调用
└─────────────────────────┘
       │
       ▼
       XssUtils.cleaning()
       │
       ▼
@RequestBody 对象中的字符串已清洗

关键设计思想

  1. 非侵入式:过滤器与 Jackson 模块均通过配置加入,业务代码零改动。
  2. 分层防御:同时覆盖 application/x-www-form-urlencodedmultipart/form-dataquery string 以及 application/json 四种常见请求格式。
  3. 可配置策略:通过修改 AntiSamy 策略文件,可灵活控制允许的标签(如保留 <b><i> 而删除 <script>)。

3. 核心组件详解

3.1 XssUtils – 底层清洗引擎

XssUtils 是防 XSS 的核心工具类,封装了 AntiSamy 的调用逻辑。采用单例模式,确保策略文件只加载一次。

private XssUtils() {
   
    Policy policy = createPolicy();
    this.antiSamy = ObjectUtils.isNotEmpty(policy) ? new AntiSamy(policy) : new AntiSamy();
    this.nbsp = cleanHtml("&nbsp;");
    this.quot = cleanHtml("\"");
}

关键特性

  • 策略加载:从 classpath antisamy/antisamy-anythinggoes.xml 读取策略文件。若加载失败(例如文件缺失),则回退到 AntiSamy 的默认策略。
  • 字符乱码修复:AntiSamy 在处理 &nbsp; 和双引号时可能产生异常字符(取决于策略和 JDK 版本)。代码预先清洗这两个字符串,然后在最终结果中用 replaceAll 将其还原或清除。
  • 实体反转义:调用 StringEscapeUtils.unescapeHtml4() 对输入先进行反转义,避免 AntiSamy 二次转义导致误判。

清洗流程cleaning 方法):

  1. 反转义 HTML 实体(如 &lt;<)。
  2. 调用 cleanHtmlantiSamy.scan()
  3. 替换 &nbsp; 产生的乱码为空字符串。
  4. 替换双引号乱码为原始双引号。
  5. 移除所有换行符(可根据需求调整)。
  6. 返回清洗后的字符串。

注意:移除换行符可能影响多行文本输入,实际项目中可移除此步骤或改为可选配置。

3.2 XssHttpServletFilter – 请求参数清洗

该过滤器实现了 javax.servlet.Filter,作用是将原始的 HttpServletRequest 包装成自定义的 XssHttpServletRequestWrapper

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
   
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(request);
    filterChain.doFilter(xssRequest, servletResponse);
}

XssHttpServletRequestWrapper会重写以下方法:

  • getParameter(String name)
  • getParameterValues(String name)
  • getParameterMap()
  • getHeader(String name)

在这些方法中,调用 XssUtils.cleaning() 对每个字符串值进行清洗,然后返回安全版本。这样一来,@RequestParam@ModelAttribute 甚至原生 request.getParameter() 获取到的数据都是已过滤的。

优点:一次配置,所有 GET/POST 表单参数自动受到保护。
缺点:可能误伤正常的 HTML 提交(如富文本编辑器内容),需要根据业务场景调整策略文件或提供绕过方式。

3.3 XssJsonDeserializer – JSON 字符串清洗

当请求体为 JSON 时,Spring 默认使用 Jackson 进行反序列化。XssJsonDeserializer 继承自 JsonDeserializer<String>,并注册到 SimpleModule 中。

public class XssJsonDeserializer extends JsonDeserializer<String> {
   
    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
   
        String value = p.getValueAsString();
        if (StringUtils.isNotBlank(value)) {
   
            return XssUtils.cleaning(value);
        }
        return value;
    }
}

任何被 Jackson 反序列化为 String 的 JSON 字段都会触发该反序列化器,从而执行 XssUtils.cleaning()

3.4 XssSimpleModule – Jackson 模块注册

XssSimpleModule 是一个 SimpleModule 子类,在构造器中绑定 String.classXssJsonDeserializer

public XssSimpleModule() {
   
    super(XssSimpleModule.class.getName(), JsonConsts.JSON_VERSION);
    this.addDeserializer(String.class, XssJsonDeserializer.instance);
}

应用启动时,只需将该模块添加到 ObjectMapper 中,即可全局生效。

4. 在 Spring Boot 中集成

4.1 添加依赖

<!-- OWASP AntiSamy -->
<dependency>
    <groupId>org.owasp.antisamy</groupId>
    <artifactId>antisamy</artifactId>
    <version>1.7.4</version>
</dependency>
<!-- Apache Commons Text (用于转义/反转义) -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.11.0</version>
</dependency>
<!-- Spring Boot Web Starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

4.2 注册 Filter

@Configuration
public class XssFilterConfig {
   
    @Bean
    public FilterRegistrationBean<XssHttpServletFilter> xssFilter() {
   
        FilterRegistrationBean<XssHttpServletFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new XssHttpServletFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

4.3 注册 Jackson Module

@Configuration
public class XssJacksonConfig {
   
    @Bean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
   
        ObjectMapper mapper = builder.build();
        mapper.registerModule(new XssSimpleModule());
        return mapper;
    }
}

Spring Boot 自动配置覆盖:若项目中已有自定义 ObjectMapper,请确保本模块被注入;也可通过 @PrimaryJackson2ObjectMapperBuilderCustomizer 添加模块。

4.4 策略文件放置

antisamy-anythinggoes.xml 存放在 src/main/resources/antisamy/ 目录下。该策略文件可从 OWASP 官方获取或自定义。

5. 测试样例

5.1 表单/URL 参数测试

Controller

@PostMapping("/comment")
public String addComment(@RequestParam String content) {
   
    return "Your comment: " + content;
}

请求

POST /comment?content=<script>alert(1)</script>Hello

响应

Your comment: Hello

<script> 标签被策略删除。

5.2 JSON 请求体测试

Controller

@PostMapping("/comment/json")
public Comment addCommentJson(@RequestBody Comment comment) {
   
    return comment;
}

public class Comment {
   
    private String author;
    private String text;
    // getters/setters
}

请求

{
   
    "author": "<img src=x onerror=alert('xss')>John",
    "text": "<b>Safe</b> &lt;script&gt;"
}

响应(清洗后):

{
   
    "author": "John",
    "text": "<b>Safe</b> &lt;script&gt;"
}

注意:&lt;script&gt; 会被反转义为 <script>,然后策略可能删除 <script> 标签但保留文本 "<script>"(取决于策略配置)。上述显示仅为示意,实际行为跟随策略。

6. 注意事项与最佳实践

6.1 性能考虑

  • AntiSamy 扫描涉及 HTML 解析,对于长文本或高并发场景可能成为性能瓶颈。可考虑缓存清洗结果(基于输入内容的哈希),或仅对包含 <>& 等危险字符的输入进行清洗。
  • XssUtils 是单例,AntiSamy 实例是线程安全的,但 Policy 对象也应重用(本代码已满足)。

6.2 富文本编辑器支持

若要允许用户提交 HTML(如博客正文),不应使用 XssHttpServletFilter 无差别清洗,而应仅在输出时转义,或使用白名单策略保留格式化标签。本方案中的 AntiSamy 策略可以配置为只删除恶意标签(如 <script>onerror 等),保留 <b><i><p> 等安全标签,因此适用于富文本场景。

6.3 绕过与白名单

绝对的安全是不存在的。AntiSamy 也会存在绕过漏洞,需定期更新策略库和 AntiSamy 版本。建议结合 CSP(Content Security Policy)输出转义 形成纵深防御。

6.4 移除换行符的影响

原始代码中 result.replaceAll("\n", "") 会删除所有换行,可能导致多行文本输入(如文本框、文本域)变成一行。建议移除该行或改为可选开关。

6.5 特殊字符的二次转义

StringEscapeUtils.unescapeHtml4() 会将 &amp; 还原为 &,这可能改变原始数据。例如用户输入 &lt; 本意是展示文本 <,反转义后变为 <,再经过 AntiSamy 可能会被当作标签处理。通常这种场景很少,但若业务需要保留字面量,可以省略反转义步骤,直接让 AntiSamy 处理原始输入。

7. 总结

本文围绕四个核心类,详细介绍了如何在 Spring Boot 应用中构建多层次的 XSS 防御体系:

  • XssUtils:基于 AntiSamy 的清洗核心,处理所有清洗逻辑。
  • XssHttpServletFilter:清洗 GET/POST 表单参数及请求头。
  • XssJsonDeserializer + XssSimpleModule:清洗 JSON 请求体中的字符串字段。

该方案已经在生产环境中得到验证,能够有效阻断常见的反射型、存储型 XSS 攻击,同时保留了合理的 HTML 标签(依策略而定)。开发者可根据自身业务需求调整策略文件,以达到安全与功能的最佳平衡。

扩展建议:未来可增加对 @RequestPart 上传文件内容的清洗,或提供注解 @NotXss 以允许个别字段绕过清洗。

目录
相关文章
|
11天前
|
人工智能 JSON 自然语言处理
让教学更智慧:用阿里云百炼工作流,自动生成中小学教材内容#小有可为#有温度的AI
通过可视化工作流编排,将大模型推理能力转化为标准化的教学内容生成引擎。教师只需输入教材标题和适用学段,即可自动获得结构完整、符合课程标准的章节内容,大幅降低备课门槛,助力教育资源均衡化。
493 126
|
21天前
|
Linux 程序员 数据格式
【2026最新】Notepad++下载、安装和使用一篇搞定(附中文版安装包)
Notepad++ 是一款免费开源、轻量高效的 Windows 文本编辑器,支持 C/Python/HTML 等 80+ 语言语法高亮、代码折叠、正则替换、编码转换及插件扩展,专为程序员与文本处理用户打造,完美替代系统记事本。(239字)
|
6天前
|
人工智能 缓存 安全
Claude Code 封号真实原因曝光,这次彻底不装了,直接针对国内开发者的账号下手?
Claude Code 封号潮背后:逆向扒出客户端隐写区域标记,Anthropic 政策收紧叠加 DeepSeek 7 月涨价,国产替代更紧迫。
|
15天前
|
存储 人工智能 监控
QoderWork完全指南:从入门到精通,把“AI实习生”变成你的全能工作搭档
阿里云2026年推出的桌面端AI工作助手QoderWork,不止聊天,更可动手干活:本地运行、安全可控,支持文件整理、数据分析、PPT生成、网页开发等;内置专家套件、多Agent协作与自定义Skills,让AI真正成为你身边的“AI实习生”。
|
6天前
|
人工智能 安全 程序员
终于,Claude Code 封号的原因被曝光了!竟然针对中国用户,植入隐形代码?!
通俗易懂地揭秘 Claude Code 封号的手段,分享一些自己对 AI 编程困境的思考,Codex、Cursor、DeepSeek、智谱 GLM、甚至是豆包,都有所行动了
399 1
|
7天前
|
人工智能 安全 Cloud Native
Higress 新发布:AI Gateway 能力增强,Gateway API 及其推理扩展持续打磨
增强 AI 网关能力,持续打磨 Gateway API 及其推理扩展。
360 127
|
15天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
899 5
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~