FlawOpen

flawopen.com/XSS in JavaScript/DOMPurify and innerHTML

DOMPurify 足以让 innerHTML 安全吗?

参考指南
简短回答

如果 DOMPurify 的输出未经后续修改就直接进入 HTML 注入点,它是可靠的富 HTML 允许列表清理器。可以把它想成货物在入库口接受检查:如果之后被重新包装或从别的入口送入,最初检查就无法覆盖最终内容。普通文本请用 textContent,避免解析 HTML。

仍有风险 — 旧清理结果进入注入点
// 使用旧版本/策略在保存时清理。
saveToDb(DOMPurify.sanitize(input));

// 后续代码未经复核就信任存储字符串。
el.innerHTML = fetchedFromDb;
更安全 — 针对 HTML 注入点清理
// 需要富 HTML 时,使用当前清理库
// 并在写入 HTML 上下文前立即处理。
el.innerHTML = DOMPurify.sanitize(untrustedHtml, {
  USE_PROFILES: { html: true },
});

// 普通文本不要作为 HTML 解析。
textNode.textContent = untrustedText;

为什么只在保存时清理不够稳妥

保存时清理本身不一定不安全,但会形成陈旧数据边界:清理器修复漏洞、策略改变或某条路径跳过清理后,渲染器仍可能信任旧输出。应在渲染时用当前版本和目标上下文的策略再次清理。插入前不要修改清理后的标记,也不要交给其他库处理;后处理可能使清理失效。

配置和输出上下文很重要

DOMPurify 默认允许 HTML、SVG 和 MathML。如果只需要 HTML,请用 USE_PROFILES: { html: true } 缩小配置。未经审查不要扩大标签或属性范围。DOMPurify 不清理 CSS,也不阻止所有外部资源请求;输出只能用于 HTML 上下文,不要复用到 JavaScript、属性、XML、SVG 或 CSS。

Trusted Types 与服务端渲染

浏览器支持时,通过 Content Security Policy 强制使用 Trusted Types,拒绝未经批准策略处理的字符串写入 HTML 注入点。DOMPurify 可以返回 TrustedHTML,这能减少误用 sink,但无法修复不安全配置或后处理。服务端使用时,DOMPurify 依赖 DOM 实现:保持 jsdom 更新,安全敏感的清理不要与 happy-dom 搭配。

生产环境检查清单

搜索 HTML 注入点

rg -n -g '*.{js,ts,jsx,tsx}' '(innerHTML|outerHTML|insertAdjacentHTML|document\.write|dangerouslySetInnerHTML)' .
逐个追踪不可信数据到注入点,确认插入前立即执行清理,并检查后续代码不会修改标记。

来源与参考

另请参阅 XSS 跨站脚本攻击(CWE-79):原理与各编程语言的防护方法 — flawopen.com Markdown 渲染能防御 XSS 吗? Svelte 默认能防止 XSS 吗? 上传 SVG 有 XSS 风险吗? Vue v-html 的 XSS 风险 WKWebView loadHTMLString 能安全显示不可信内容吗? 反射型、存储型与 DOM 型 XSS 有何区别?