# 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):富文本白名单清洗
依赖:
jsoup- 位置:pom.xml
4.2 请求体统一清洗
位置:
职责:
- 拦截
@RequestBody反序列化后的对象 - 递归处理对象字段、数组、列表中的字符串
- 默认按普通文本清洗
- 标注
@XssRichText的字段按富文本白名单清洗 - 标注
@XssIgnore的字段跳过清洗
4.3 查询参数 / 表单参数统一清洗
位置:
职责:
- 包装
HttpServletRequest - 对
getParameter、getParameterValues、getParameterMap返回值做普通文本清洗 - 跳过
multipart/form-data,避免影响文件上传
4.4 安全响应头
已补充:
X-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-origin
位置:
5. 注解说明
5.1 @XssRichText
位置:
含义:
- 当前字段允许保存安全富文本
- 字段值会走
cleanRichText
适用字段:
- 协议正文
- 文章正文
- 商品详情描述
5.2 @XssIgnore
位置:
含义:
- 当前字段跳过统一 XSS 清洗
使用要求:
- 只有在字段内容本身不是最终展示文本,或者已有专门的业务级校验时才允许使用
- 使用前必须评估是否会形成新的存储型 XSS 入口
6. 当前已接入的业务字段
6.1 协议内容
- AgreementConfig.java
- 字段:
agreementContent - 模式:富文本白名单清洗
6.2 文章正文
- ArticleInfo.java
- 字段:
articleContent - 模式:富文本白名单清洗
6.3 商品描述
- GoodsSpu.java
- 字段:
description - 模式:富文本白名单清洗
6.4 供应商商品描述
- SupplierGoodsSpu.java
- 字段:
description - 模式:富文本白名单清洗
7. 富文本白名单规则
当前富文本基于 Jsoup Safelist.relaxed() 扩展,额外允许了以下结构类标签:
sectionarticleheaderfootermainspandivtabletheadtbodytfoottrthtdcolgroupcol
额外允许的属性:
- 全标签:
class - 链接:
target、rel - 表格:
colspan、rowspan
允许的协议:
a[href]:http、https、mailto、telimg[src]:http、https、data
说明:
- 事件属性如
onclick、onerror会被移除 javascript:这类危险协议会被移除- 不在白名单内的标签会被过滤
8. 开发接入规范
8.1 新增普通文本字段
默认无需额外处理。
原因:
@RequestBody下的普通String字段已经会被统一清洗- 查询参数、表单参数已经会被过滤器统一清洗
8.2 新增富文本字段
做法:
- 在字段上标注
@XssRichText - 确认该字段确实需要保存 HTML
- 确认回显端不会再做危险拼接
示例:
@XssRichText
private String content;
8.3 确需跳过清洗的字段
做法:
- 先确认字段不是直接面向前端展示的文本
- 再标注
@XssIgnore - 在代码评审中说明原因
示例:
@XssIgnore
private String rawTemplate;
9. 当前边界与限制
当前方案是“基础防护”,还不是“全链路零死角”。
9.1 未递归清洗动态 JSON 结构
为了避免误伤任意配置类内容,以下类型当前默认跳过递归清洗:
MapJSONObjectJSONArray
影响:
- 某些业务如果把富文本嵌在 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. 后续建议
建议按以下顺序继续完善:
- 盘点所有富文本字段,继续补
@XssRichText - 对
JSONObject / Map类配置内容建立定向清洗规则 - 对历史数据做存量扫描和修复
- 前端侧补危险渲染点排查,禁止无约束的
v-html - 条件成熟后再推进 CSP
12. 总结
当前项目的 XSS 防护已经从“无统一防护”升级为“具备统一基础防护能力”:
- 请求参数已统一过滤
@RequestBody已统一清洗- 富文本字段可按白名单保存安全 HTML
- 关键安全响应头已补齐
后续新增内容类字段时,开发人员只需要判断它属于:
- 普通文本
- 富文本
- 特殊跳过
然后按本文档接入即可。