← 中文资源

简体中文版本

dangerouslySetInnerHTML 导致 XSS — 未转义 HTML

通过正常渲染文本、对确需的 HTML 做净化,并依靠 React 默认转义,修复 dangerouslySetInnerHTML 导致的 XSS。

阅读英文原文

症状

本篇指南处理“dangerouslySetInnerHTML 导致 XSS — 未转义 HTML”。请从症状出发,按顺序执行诊断,在推进前确认每个假设;随后通过修复与验证步骤闭环。

可观察到的症状包括:

  • 经 dangerouslySetInnerHTML 注入的用户内容执行了脚本
  • 存储的评论渲染出未净化的 HTML
  • 扫描报告 DOM XSS 汇聚点

诊断

先在隔离环境中复现故障,再按顺序执行以下检查,逐步缩小范围。

步骤 1

grep -rn "dangerouslySetInnerHTML\|innerHTML\|v-html" src/

每个接收用户内容的汇聚点都是潜在 XSS 向量。

步骤 2

git grep -n "<img src=x onerror" || echo 'no literal payload committed'

在预发环境渲染受控负载可验证脚本是否执行。

常见诱因(按出现频率排序):

  • 把用户控制的 HTML 传给 dangerouslySetInnerHTML
  • 应用了净化器但白名单过于宽松
  • Markdown/富文本转换后未净化 HTML 即渲染

根因

dangerouslySetInnerHTML 会注入原始 HTML,绕过 React 的转义。当它接收用户控制的内容时,攻击者可加入事件处理器或 script 标签,在其他用户会话中执行。

修复

把不可信内容作为文本渲染;当确需 HTML 时,用经过验证的库与严格的标签/属性白名单先净化再注入,或在沙箱 iframe 中渲染。

注意:自实现的净化器与正则过滤容易被绕过;应使用如 DOMPurify 这类维护良好的库。

import DOMPurify from 'dompurify';

// ❌ raw user HTML
// <div dangerouslySetInnerHTML={{ __html: userHtml }} />

// ✅ sanitize with a strict allowlist, then inject
const clean = DOMPurify.sanitize(userHtml, {
  ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
  ALLOWED_ATTR: ['href'],
});
// <div dangerouslySetInnerHTML={{ __html: clean }} />

验证

再次渲染负载并确认无脚本执行且安全标记仍显示;验证净化器移除 onerror 与 script 标签。

修复后补充一条回归测试,复现原始症状,防止问题再次出现。

这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。