FlawOpen

flawopen.com/JavaScript 中的 XSS/dangerouslySetInnerHTML

React 的 dangerouslySetInnerHTML 安全吗?

参考页面
简短回答

把普通 JSX 想成标签打印机:文本仍是文本。dangerouslySetInnerHTML 则像一台会把字符串解析成 HTML 的印刷机。如果字符串包含攻击者可控的内容,浏览器可能将其视为可执行的标记。只有在 HTML 已明确可信,或已针对准确的 HTML 上下文完成清理时,才使用这个输出点。

为什么普通 JSX 不需要它

有风险 — 未处理的 HTML
function Comment({ html }) {
  return <p dangerouslySetInnerHTML=
    {{ __html: html }} />;
}
安全 — 普通 JSX 插值
function Comment({ text }) {
  return <p>{text}</p>;
}

何时适合使用 dangerouslySetInnerHTML?

仅当产品必须保留无法用普通 React 子节点表示的受限富文本格式时才使用。CMS、编辑器或 Markdown 渲染器并不会自动可信:存储内容和渲染器输出仍可能包含不可信标记。定义允许的元素和属性,使用持续维护的 HTML 清理库(如 DOMPurify)清理,并将结果直接传入该输出点。

正确使用方式

在渲染边界清理

在赋值给 dangerouslySetInnerHTML 之前立即使用 DOMPurify 等持续维护的库清理内容。不要用正则表达式清理 HTML。清理后避免再修改字符串;后续转换可能破坏清理器的安全保证。及时更新清理库及其解析环境。

限制允许的标记

仅禁止已知标签的黑名单并不完整:风险也可能来自属性、URL 字段、命名空间和解析器行为。根据功能需要列出少量允许的元素和属性,并按该策略配置清理器。还应根据产品允许的目标单独检查链接和其他包含 URL 的属性。

如何检查代码

grep -rn "dangerouslySetInnerHTML" --include="*.jsx" --include="*.tsx" .
对每个匹配项,追踪值的来源和所有转换。将用户输入、导入内容和存储的富文本视为不可信;确认清理策略适用于输出上下文,并且之后没有步骤修改清理结果。

常见问题

Markdown 渲染会自动保证安全么?

这取决于渲染器及其配置。检查是否接受原始 HTML、是否清理输出;仅转换 Markdown 并不会自动使 HTML 安全。

这也适用于 Vue 的 v-html 吗?

适用。v-html 同样会绕过框架通常的文本插值,将 HTML 交给浏览器。应将其视为 HTML 输出点,并按适当策略清理不可信内容。

参考资料

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