【実務・中級編】ReactにおけるJSXの自動エスケープとdangerouslySetInnerHTMLの危険性 – アプリケーションセキュリティ & 安全な開発防御ガイド

Reactの「自動エスケープ」を過信するな。dangerouslySetInnerHTMLが引き起こす悪夢と防衛術

やあ。現場の最前線でコードを書き、時には炎上したインシデントの火消しに走るエンジニア諸君。今日はReact開発で誰もが一度は誘惑に駆られる、「HTMLの直接レンダリング」という禁忌について話そう。

「ReactはJSXで自動エスケープしてくれるから安全だよね?」という言葉を耳にするたびに、私は背筋が凍る思いがする。確かにReactはデフォルトで文字列をエスケープする。だが、その安心感こそが、攻撃者にとっての「一番の餌場」になっていることを知っているか?

1. なぜReactは「安全」だと言われているのか?

ReactのJSXは、埋め込まれた変数を文字列としてレンダリングする際に、自動的にエスケープ処理を行う。つまり、という文字列を {userInput} として渡せば、そのままブラウザ上に文字列として表示されるだけで、スクリプトは実行されない。これがReactの「デフォルトの防御力」だ。

しかし、現実はそう甘くない。CMSから取得したHTMLや、リッチテキストエディタの出力など、「どうしてもHTMLタグとしてレンダリングしたい」という要件に直面したとき、多くのエンジニアがこの悪魔の関数に手を出す。

dangerouslySetInnerHTML という劇薬

この関数名を見てくれ。Reactの公式ドキュメントですら「危険だ」と警告しているのに、なぜか開発者は「まあ、サニタイズしてるし大丈夫だろう」と高を括る。これがすべての始まりだ。

2. 攻撃者が狙う「盲点」:PoC(概念実証)

例えば、ユーザーのプロフィール編集画面で、管理者がHTML入力を許可している場合を想像してほしい。

// 危険な実装例
function UserProfile({ bioHtml }) {
// 開発者の思考:「サーバー側でサニタイズしてるから大丈夫」
return

;
}

この時、攻撃者は以下のようなペイロードを送り込む。

もしサーバーサイドのサニタイズが甘ければ、onerror イベントが発火し、あなたのWebアプリのセッションIDが攻撃者のサーバーへと送信される。これがクロスサイトスクリプティング(XSS)の真骨頂だ。「クライアント側の防御」を「サーバー側のチェック」の代わりにしてはいけない。

3. 完全防御のためのセキュアな実装

では、どうすればいいか?答えはシンプルだ。「HTMLを直接レンダリングしない」か、「徹底的に無害化する」かの二択だ。

推奨:DOMPurifyによる防御

どうしてもHTMLをレンダリングする必要がある場合は、dompurify ライブラリを使って、レンダリング直前に「安全なタグだけ」を通すフィルタリングを必ず実装しろ。

import DOMPurify from ‘dompurify’;

function SafeHtmlRenderer({ htmlContent }) {
// 1. DOMPurifyで悪意のある属性(onerrorなど)を徹底的に除去
// 2. USE_PROFILES: { html: true } でHTMLのみに限定
const cleanHtml = DOMPurify.sanitize(htmlContent, {
ALLOWED_TAGS: [‘b’, ‘i’, ‘em’, ‘strong’, ‘a’], // 許可するタグを最小限にする
ALLOWED_ATTR: [‘href’] // 許可する属性も最小限に
});

return (


);
}

4. 運用レイヤーでの多層防御(WAFの活用)

コードレベルの修正だけでは、ゼロデイ攻撃には対応できない。インフラエンジニアの視点も交えて、WAF(Web Application Firewall)での防御も必須だ。

例えば、AWS WAFを使用しているなら、以下のルールを適用することを強く推奨する。

  • Core Rule Set (CRS) の有効化: XSSシグネチャを検知するルールを必ずONにする。
  • カスタムルール: dangerouslySetInnerHTML を使うようなエンドポイントに対し、
securityintronationalをフォローする

コメント

タイトルとURLをコピーしました