JooLun Plus 百科 ⬇ 源码下载

03.12 XSS 防护开发文档

102353 于 2026-10-17 14:14:33 +08:00 修改了此页面

# XSS防护开发文档

1. 文档目的

本文档用于说明 joolun-plus 当前已落地的 XSS 防护方案,包括:

  • 防护目标与边界
  • 已接入的公共能力
  • 业务字段接入方式
  • 当前已覆盖范围
  • 后续扩展与注意事项

本文档对应当前仓库内已经实现的基础 XSS 防护代码,而不是单纯的设计稿。

2. 背景说明

在本项目中,存在以下典型 XSS 风险来源:

  • 管理端录入富文本内容后,在商城端、小程序、H5、WebView 中回显
  • 查询参数、表单参数被直接拼接或回显
  • 协议、文章、商品描述等内容被保存到数据库后形成存储型 XSS

本次治理的目标不是引入一个“万能黑名单”,而是建立一套可维护的规则:

  • 普通字符串按纯文本处理
  • 明确允许富文本的字段按白名单处理
  • 查询参数和表单参数做统一过滤
  • 安全响应头补齐基础能力

3. 防护目标与范围

当前方案重点覆盖以下两类场景:

3.1 普通文本输入

适用于:

  • 查询条件
  • 表单普通文本框
  • DTO / Entity 中的普通 String

处理方式:

  • 去除 HTML 标签
  • 去除 script、style、iframe、object、embed
  • 最终按纯文本保留

3.2 富文本输入

适用于:

  • 协议内容
  • 文章正文
  • 商品描述

处理方式:

  • 使用白名单保留安全 HTML 标签
  • 移除危险属性和危险协议
  • 对 a[target] 自动补 rel="noopener noreferrer"

4. 总体实现方案

当前 XSS 防护分为 4 层:

4.1 公共清洗工具

位置:

职责:

  • cleanPlainText(String):普通文本清洗
  • cleanRichText(String):富文本白名单清洗

依赖:

4.2 请求体统一清洗

位置:

职责:

  • 拦截 @RequestBody 反序列化后的对象
  • 递归处理对象字段、数组、列表中的字符串
  • 默认按普通文本清洗
  • 标注 @XssRichText 的字段按富文本白名单清洗
  • 标注 @XssIgnore 的字段跳过清洗

4.3 查询参数 / 表单参数统一清洗

位置:

职责:

  • 包装 HttpServletRequest
  • 对 getParameter、getParameterValues、getParameterMap 返回值做普通文本清洗
  • 跳过 multipart/form-data,避免影响文件上传

4.4 安全响应头

已补充:

  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin-when-cross-origin

位置:

5. 注解说明

5.1 @XssRichText

位置:

含义:

  • 当前字段允许保存安全富文本
  • 字段值会走 cleanRichText

适用字段:

  • 协议正文
  • 文章正文
  • 商品详情描述

5.2 @XssIgnore

位置:

含义:

  • 当前字段跳过统一 XSS 清洗

使用要求:

  • 只有在字段内容本身不是最终展示文本,或者已有专门的业务级校验时才允许使用
  • 使用前必须评估是否会形成新的存储型 XSS 入口

6. 当前已接入的业务字段

6.1 协议内容

6.2 文章正文

6.3 商品描述

  • GoodsSpu.java
  • 字段:description
  • 模式:富文本白名单清洗

6.4 供应商商品描述

7. 富文本白名单规则

当前富文本基于 Jsoup Safelist.relaxed() 扩展,额外允许了以下结构类标签:

  • section
  • article
  • header
  • footer
  • main
  • span
  • div
  • table
  • thead
  • tbody
  • tfoot
  • tr
  • th
  • td
  • colgroup
  • col

额外允许的属性:

  • 全标签:class
  • 链接:target、rel
  • 表格:colspan、rowspan

允许的协议:

  • a[href]:http、https、mailto、tel
  • img[src]:http、https、data

说明:

  • 事件属性如 onclick、onerror 会被移除
  • javascript: 这类危险协议会被移除
  • 不在白名单内的标签会被过滤

8. 开发接入规范

8.1 新增普通文本字段

默认无需额外处理。

原因:

  • @RequestBody 下的普通 String 字段已经会被统一清洗
  • 查询参数、表单参数已经会被过滤器统一清洗

8.2 新增富文本字段

做法:

  1. 在字段上标注 @XssRichText
  2. 确认该字段确实需要保存 HTML
  3. 确认回显端不会再做危险拼接

示例:

@XssRichText
private String content;

8.3 确需跳过清洗的字段

做法:

  1. 先确认字段不是直接面向前端展示的文本
  2. 再标注 @XssIgnore
  3. 在代码评审中说明原因

示例:

@XssIgnore
private String rawTemplate;

9. 当前边界与限制

当前方案是“基础防护”,还不是“全链路零死角”。

9.1 未递归清洗动态 JSON 结构

为了避免误伤任意配置类内容,以下类型当前默认跳过递归清洗:

  • Map
  • JSONObject
  • JSONArray

影响:

  • 某些业务如果把富文本嵌在 JSON 结构内部,当前不会被自动处理
  • 典型例子是部分微信图文消息内容、页面配置 JSON

建议:

  • 后续针对明确结构的 JSON 内容增加定向清洗逻辑

9.2 未引入 CSP

当前补的是基础安全响应头,没有统一落地 Content-Security-Policy。

原因:

  • 现有系统前后端依赖较多
  • 直接加 CSP 可能影响脚本、样式、富文本展示、第三方资源加载

建议:

  • 后续按前端实际资源模型单独做 CSP 方案评估

9.3 历史脏数据不会自动回刷

当前方案只处理新增和更新请求。

影响:

  • 数据库中已经存在的旧 HTML / 脚本内容不会自动被清洗

建议:

  • 对协议、文章、商品描述等表执行一次历史数据巡检和修复

10. 测试与验证

10.1 单元测试

位置:

覆盖内容:

  • 普通文本去脚本
  • 富文本保留安全标签
  • 富文本移除危险属性和危险协议

10.2 构建验证命令

mvn -pl joolun-common/joolun-common-core -am -Dtest=XssUtilsTest test
mvn -pl joolun-common/joolun-common-security,joolun-upms/joolun-upms-common,joolun-mall/joolun-mall-common,joolun-auth,joolun-monitor -am -DskipTests package

11. 后续建议

建议按以下顺序继续完善:

  1. 盘点所有富文本字段,继续补 @XssRichText
  2. 对 JSONObject / Map 类配置内容建立定向清洗规则
  3. 对历史数据做存量扫描和修复
  4. 前端侧补危险渲染点排查,禁止无约束的 v-html
  5. 条件成熟后再推进 CSP

12. 总结

当前项目的 XSS 防护已经从“无统一防护”升级为“具备统一基础防护能力”:

  • 请求参数已统一过滤
  • @RequestBody 已统一清洗
  • 富文本字段可按白名单保存安全 HTML
  • 关键安全响应头已补齐

后续新增内容类字段时,开发人员只需要判断它属于:

  • 普通文本
  • 富文本
  • 特殊跳过

然后按本文档接入即可。