接口html安全过滤核心是可信上下文中可控渲染,需后端dom级白名单净化(如dompurify)、前端二次校验、按渲染位置选方式(innerhtml/setattribute/json.stringify),并辅以csp等纵深防御。

对接口返回的 HTML 片段做安全过滤,核心不是“删掉所有标签”,而是**在可信上下文中可控地渲染**。直接用 innerHTML 插入未经处理的 HTML 是 XSS 高危操作,必须分环节控制:后端入库前净化 + 前端接收后二次校验 + 渲染时按语境选方式。
后端入库前必须做 DOM 级白名单净化
前端再怎么过滤,攻击者也能绕过(比如 curl 直接发请求)。所以第一道防线必须在服务端完成:
- 使用 DOMPurify(Node.js)或 jsoup(Java)、bleach(Python)等基于真实 DOM 解析的库,而非正则替换
- 配置显式白名单:只允许
p、br、strong、a、img等基础标签;只保留href、src、alt等必要属性 - 对
href和src值强制协议白名单(仅允许http://、https://、/、#),禁用javascript:、data:、vbscript: - 存储到数据库的是已净化后的 HTML 字符串,不是原始输入
前端收到 HTML 后不直接 innerHTML,先过一遍 sanitize
即使后端已处理,前端仍需二次净化——防止中间劫持、CDN 缓存污染或 API 被其他客户端复用时出问题:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 浏览器环境直接引入 DOMPurify:
import DOMPurify from 'dompurify'; - 调用
DOMPurify.sanitize(dirtyHtml, { ALLOWED_TAGS: ['p','a','img'], ALLOWED_ATTR: ['href','src','alt'] }) - 不要依赖默认配置:它默认保留
style和class,可能引发 CSS 注入(如style="background:url(javascript:alert())") - 净化后结果再赋值给
innerHTML,或交给框架处理(Vue 的v-html、React 的dangerouslySetInnerHTML)
渲染位置决定安全写法,不能一招鲜
同一段 HTML,在不同位置插入,风险和处理方式完全不同:
- 插入到普通内容区(如
<div id="content"></div>)→ 用element.innerHTML = cleanHtml,前提是 cleanHtml 已由 DOMPurify 处理过 - 插入到属性值中(如
<div title="{{ html }}">)→ 绝不能直接拼字符串,改用 <code>element.setAttribute('title', text),浏览器会自动转义 - 插入到 JavaScript 字符串里(如
var msg = "{{ html }}";)→ 必须用JSON.stringify(html)包裹,它会正确转义引号、反斜杠和控制字符 - 若用 Vue/React,优先走模板语法(
{{ content }}或{content}),它们默认 HTML 转义;仅当明确需要富文本时,才启用v-html或dangerouslySetInnerHTML,且内容必须已净化 - 设置 Content-Security-Policy 响应头,禁止内联脚本和未授权外域资源,例如:
script-src 'self'; object-src 'none'; base-uri 'self' - API 返回 HTML 时加
X-Content-Type-Options: nosniff,防 MIME 类型嗅探绕过 - Via 框架能力简化风险:Vue 3 的
v-html可配合自定义指令做运行时校验;React 可封装SafeHtml组件,内部自动调用 DOMPurify - 避免把 HTML 当通用字段传——如果只是展示纯文本,就让后端返回 plain text;如果只是展示链接,就拆成
{url: "...", text: "..."}结构,前端拼 DOM,彻底规避 HTML 解析
补充加固:CSP + HTTP 头 + 框架能力
纵深防御不能只靠一层:










