「React 脆弱性」で検索すると、JVN や CVE のデータベース、個別の CVE 速報が並ぶ。だが実務で手が止まるのは、多くの場合 react / react-dom 本体のバグではない。本記事は react・react-dom 19.3.1 を最小構成で動かして npm audit と描画の挙動を実測し、現場で言う「React 脆弱性」の実体を XSS・依存・SSR・古い版の4層に分けて整理する。特定の新しい CVE に乗らず、長く参照できる形で残す。

WHAT IT MEANS言葉の中身

React 本体の脆弱性は稀、実体は本体の外側にある

React 本体(react / react-dom)

JSX は既定で文字列をエスケープ

  • 公開アドバイザリは主に Server Components 層
  • クライアント描画の自前コードの勧告は稀

現場で言う「React 脆弱性」

多くは本体の外の層

  • XSS を生む書き方/依存パッケージ
  • SSR・フレームワーク/古い版
出典: 本記事の実測(npm audit・react-dom/server・facebook/react の公開アドバイザリ、2026-10-21時点)

30秒でわかる(2026-10-21時点)

  • ・本体の勧告は稀:facebook/react の公開アドバイザリは10件で、いずれも Server Components 層
  • ・実体は4層:XSS を生む書き方/依存パッケージ/SSR・フレームワーク/古い版
  • ・実測:クリーンな react/react-dom 19.3.1 で npm audit は0件。脆弱な依存を1つ足すと critical 1件
  • ・JSX は既定で守る:ただし dangerouslySetInnerHTML は素通し。ref や SSR の文字列結合も抜け道

まずやること(運用者向け・順番)

  • ・①版を確かめる:npm ls react react-dom で実際に入っている版を見る(古い版なら層4)
  • ・②既知の脆弱性を洗う:npm audit を走らせ、critical / high から仕分ける
  • ・③書き方を点検する:dangerouslySetInnerHTML を使っている箇所を洗い出し、入れる文字列の出所を確かめる

依存の乗っ取りそのものの手口は サプライチェーン攻撃とは|手口・防御ツール比較・npm 12の新機構まで実践解説 で、npm audit の結果の仕分け方は npm audit の結果はどれから直す?依存関係の脆弱性を到達性・EPSS・KEVで仕分ける実践手順 で扱った。本記事は、その手前にある「React 脆弱性という言葉の中身」を分解する。

「React 脆弱性」の多くは本体ではなく別の層にある

まず言葉を分ける。React 本体(npm の react と react-dom)に重大な脆弱性が出ることは稀だ。一方、現場で「React 脆弱性」と呼ばれる事象の実体は、本体の外側にある4つの層に散らばっている。

FOUR LAYERS4つの層

「React 脆弱性」の実体は4つの層に分かれる

  1. 層1

    XSS を生む書き方

    dangerouslySetInnerHTML・javascript: URL・ref での DOM 直接操作・SSR の文字列結合

  2. 層2

    依存パッケージ

    間接依存に入る既知の脆弱性。npm audit が見る範囲

  3. 層3

    SSR・フレームワーク

    Next.js・react-router など React を使う側の脆弱性。本体と混同されやすい

  4. 層4

    古い版

    サポート外の版や既知の問題を抱えたまま使い続ける

出典: 本記事の整理(2026-10-21時点)

この4層は責任の所在も直し方も違う。層1は自分のコードの書き方、層2と層4はパッケージの版、層3は React を使うフレームワーク側だ。「React が危ない」とひとくくりにすると、直す場所を取り違える。以下、層ごとに実測で確かめる。

層1:XSS で React が守る所と抜ける所

React は JSX に渡した文字列を既定でエスケープする。これが「React を使うと XSS が減る」と言われる根拠だが、エスケープを外す経路が複数あり、そこが層1の実体だ。react-dom 19.3.1 の renderToStaticMarkup に同じ悪意ある文字列を4通りで渡して、どこがエスケープされどこが抜けるかを確かめた。

MYTH VS REALITY誤解と実態

React は既定で守るが、抜け道は残る

よくある誤解

実際の挙動(react-dom 19.3.1 で実測)

よくある誤解: JSX に入れた文字列はそのまま危険

実際の挙動(react-dom 19.3.1 で実測): 既定でエスケープ。imgタグの文字列は無害化される

よくある誤解: href に入れた値はそのまま出力される

実際の挙動(react-dom 19.3.1 で実測): javascript: の URL は React が throw に置換してブロック

よくある誤解: React を使えば XSS は起きない

実際の挙動(react-dom 19.3.1 で実測): dangerouslySetInnerHTML は素通し。ref で DOM を直接触る経路も残る

出典: react-dom/server の renderToStaticMarkup、react-dom 19.3.1(2026-10-21実測)

実測した結果は次のとおり。入力は <img src=x onerror="alert(1)"> という典型的な XSS 文字列。

渡し方 出力(実測) 判定
JSX のテキスト子要素 &lt;img src=x onerror=... にエスケープ 既定で無害化
属性値(title など) 同じくエスケープ 既定で無害化
dangerouslySetInnerHTML <img src=x onerror=...> がそのまま 素通し
href="javascript:alert(1)" React が throw する URL に置換 React がブロック

注目は下2行だ。dangerouslySetInnerHTML は名前のとおりエスケープを外すので、信頼できない文字列を渡せばそのまま DOM に入る。一方 href に入れた javascript: スキームは、react-dom 19.3.1 では javascript:throw new Error('React has blocked a javascript: URL as a security precaution.') に置き換えられ、実行されなかった。つまり javascript: URL は React 本体が止めるが、dangerouslySetInnerHTML は止めない——同じ「XSS」でも、React が面倒を見る範囲とそうでない範囲が分かれる。

ただし React がブロックするのは、あくまで JSX 経由で属性に入れた場合だ。ref で取得した DOM ノードに innerHTML や setAttribute で直接書き込む経路、サーバー側で React の外で HTML 文字列を結合して差し込む経路は、React のエスケープを通らない。層1は「React に任せきれる所」と「自分で無害化する所」を分けて扱うのが要点になる。

層2〜4:依存パッケージ・SSR/フレームワーク・古い版

残りの3層は版とフレームワークの話だ。実際に npm audit を陰性・陽性の対照つきで走らせて、何がどう出るかを確かめた。

層2(依存パッケージ)。react と react-dom だけを入れたクリーンなアプリで npm audit を走らせると found 0 vulnerabilities だった。そこに既知の脆弱性を持つ [email protected] を1つ足すと、結果は critical 1件(Prototype Pollution)に変わる。このとき報告に現れるのは minimist であって、react でも react-dom でもない。実務の「React 脆弱性」の多くがこの層、つまり React を使うアプリが抱える周辺パッケージの既知の脆弱性だ。仕分けの手順は npm audit の記事 に譲る。

層3(SSR・フレームワーク)。Next.js や react-router など「React を使う側」の脆弱性は、見出しに React の名が出るため本体と混同されやすい。たとえば React Router v7 RCE脆弱性 CVE-2026-42211 は react-router の問題で、react / react-dom 本体ではない。直すのはそのフレームワークの版であって、React の版ではない。

層4(古い版)。サポート外の古い版を使い続けると、本体ではなくその版が引き込む依存で警告が出ることがある。react 16.0.0 を入れて npm audit を走らせると、react と react-dom 自体が high と報告された。だが理由は本体のコードではなく「脆弱な版の fbjs に依存している」ため——古い版が古い周辺パッケージを連れてくる、という形だった。版を上げれば消える種類の警告だ。

層 実体 確かめ方 直す対象
層1 XSS 書き方(dangerouslySetInnerHTML など) コードの点検・サニタイズ 自分のコード
層2 依存 周辺パッケージの既知の脆弱性 npm audit 該当パッケージの版
層3 SSR/FW Next.js・react-router 側 各フレームワークの告知 フレームワークの版
層4 古い版 サポート外の版が連れる依存 npm ls react と版の確認 React と周辺の版

React 脆弱性を自分のプロジェクトで確かめる手順

ここまでを自分のプロジェクトに当てる手順をまとめる。すべて読み取りだけのコマンドで、設定は変えない。層ごとに対策(直す場所)が違うので、まず現状を見てから手を入れる。

まず、実際に入っている版を見る。package.json の宣言ではなく、解決後の版を確かめる。

npm ls react react-dom

クリーンな構成では react-dom が react を deduped で参照する木が出る。ここで古い版が出れば層4の候補だ。次に既知の脆弱性を洗う。

npm audit
npm audit fix --dry-run

npm audit fix --dry-run は、実際には変えずに「自動で直せるか」を見せる。実測では、[email protected] のように修正版が宣言した依存範囲の外にある場合、--dry-run は up to date と表示して何もしなかった。npm audit fix が黙って直せるのは semver の範囲内に収まる修正だけで、範囲をまたぐ修正は --force が要る。audit が「fix available via npm audit fix –force」と書いたら、それは互換性が壊れうる変更なので、自動では直さず自分で版を確かめる合図だ。

最後に層1の書き方を洗い出す。

grep -rn "dangerouslySetInnerHTML" src/

このコマンドが見ているもの(過大解釈しない)

  • ・見ているのは dangerouslySetInnerHTML を使っている箇所であって、安全かどうかの判定ではない。ヒットしても安全とは言えないし、危険とは限らない。各行で「入れている文字列の出所」を1件ずつ確かめる
  • ・陽性(使っている)ならヒット、陰性(使っていない)なら何も出ずに終了(exit 1)することを手元で確かめた
  • ・ref 経由の直接 DOM 操作やサーバー側の文字列結合はこの grep には出ない。層1はこの結果だけでは判断できない

React 本体のアドバイザリは実際どれくらいあるか

「本体の勧告は稀」の根拠を示す。facebook/react の公開セキュリティアドバイザリは2026-10-21時点で10件だった。そして10件とも React Server Components / Server Functions に関するもので、最新の Critical は GHSA-xqc2-xf4c-x3h3(Unauthenticated RCE、CVSS v4 で 9.5、2026-10-14 公開)だ。

ただしこれは「React 本体に脆弱性はゼロ」という意味ではない。本体の勧告は存在するが、その多くはクライアントの描画そのものではなくサーバー側の新しい層(Server Components)に集まっている。実際 GHSA-xqc2-xf4c-x3h3 の影響範囲は react-server-dom-webpack / turbopack / parcel で、クライアント描画の react-dom 単体は対象外とされ、RSC をフレームワークやバンドラ経由で使う構成でのみ成立する。サーバーを持たない、または RSC を使わないアプリは影響しないと告知されている。なお同アドバイザリの修正版の一つが react-server-dom-* の 19.3.1 で、本記事で実測した react-dom 19.3.1 と版番号がそろう。

逆に言えば、サーバー側で RSC を使う構成では本体側のアドバイザリも現実の対象になる。「本体は安全だから読まなくてよい」ではなく、自分の構成が本体のアドバイザリの前提(RSC やサーバー実行)に当てはまるかを確かめるのが正しい付き合い方だ。アドバイザリの原文は参照ソースの facebook/react のページで版ごとに確認できる。

まとめ

「React 脆弱性」は一つのものではない。本体の勧告は稀で主にサーバー層に集まる一方、実務で遭遇するのは XSS を生む書き方・依存パッケージ・SSR/フレームワーク・古い版という本体の外の4層だ。やることは、入っている版を見て(npm ls)、既知の脆弱性を洗い(npm audit)、書き方を点検する(dangerouslySetInnerHTML の箇所を確かめる)——層ごとに直す場所を取り違えないことに尽きる。

この記事の結論

  • ・本体だけ更新しても足りない:周辺の依存とフレームワークの版、自前コードの書き方を層ごとに点検する
  • ・audit が0件=安全ではない:XSS の書き方は audit の範囲外。コードの点検を別に行う

参照ソース

・facebook/react Security Advisories — 公開アドバイザリ10件と GHSA-xqc2-xf4c-x3h3 の影響範囲(2026-10-21確認)
・React 公式ドキュメント dangerouslySetInnerHTML — エスケープを外す経路の定義(2026-10-21確認)