FlawOpen

flawopen.com/JavaScriptのXSS/dangerouslySetInnerHTML

ReactのdangerouslySetInnerHTMLは安全?

リファレンスページ
要点

通常のJSXはラベルプリンターのようなものです。テキストはテキストのまま表示されます。dangerouslySetInnerHTMLは、文字列をHTMLとして解釈する印刷機に切り替える操作です。攻撃者が制御できる内容が含まれると、ブラウザーがアクティブなマークアップとして扱う可能性があります。明確に信頼できる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レンダラーが自動的に信頼できるわけではありません。保存済みの内容やレンダラーの出力にも信頼できないマークアップが含まれることがあります。許可する要素と属性を定義し、DOMPurifyなど保守されているHTMLサニタイザーで処理した結果を、そのままこの出力箇所に渡します。

安全に使う方法

描画境界でサニタイズする

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 は未信頼コンテンツに安全?