サーバ関数の上限
@lazarv/react-serverは、すべての受信RSCリプライ(サーバ関数呼び出し)に対して、サーバ関数のコードが実行される前にリソース上限を強制します。これらの上限はペイロードのデシリアライズコストを制限し、深くネストされた値、巨大な文字列、巨大なBigInt、無制限のストリームなど、リッチなRSCリプライワイヤフォーマットを悪用するDoS攻撃からサーバを保護します。
デフォルト値は、CVE後の強化(CVE-2025-55182および関連する強化)でReact本体に導入された上限と整合しています。多くのアプリケーションでは変更する必要はありません。これらの設定は、リスクの高い公開エンドポイントで上限をより厳しくしたい場合や、正当に大きなサーバ関数ペイロード(ファイルアップロード、バッチ更新、大きなバインド引数配列など)を許可するために緩める必要がある場合に役立ちます。
リプライデコーダはサーバ関数呼び出しを処理する最初のステップとして実行されます。ワイヤペイロード(JSON文字列またはmultipart/form-dataのFormData)を解析し、以下の上限を強制してから、解決済みのサーバ関数に呼び出しをディスパッチします。
リクエストがいずれかの上限を超えると、デコーダはDecodeLimitErrorをスローし、標準のサーバ関数エラーパスを通じてクライアントに伝播されます。サーバ関数のコードは呼び出されません。エラーには超過した上限の名前と観測値が含まれており、運用上のトリアージが容易になります。
上限はデコーダ内で強制されるため、以下の両方をカバーします:
- フォーム送信(プログレッシブエンハンスメント、
<form action={…}>、useActionState) - JS駆動のサーバ関数呼び出し(クライアントコンポーネントにインポートされたサーバ関数、プロップとして渡されたサーバ関数)
| 上限 | デフォルト | 制限内容 |
|---|---|---|
maxRows | 10000 | リプライあたりのアウトライン行(チャンク)数 |
maxDepth | 128 | 行の値ツリーをマテリアライズする際の再帰深度 |
maxBytes | 32 MiB | 合計ペイロードサイズ(FormDataエントリのサイズ合計) |
maxBoundArgs | 256 | サーバリファレンスのバインド引数の数(Reactと同じ) |
maxBigIntDigits | 4096 | デコードされたBigIntリテラルの桁数(Reactと同じ) |
maxStringLength | 16 MiB | デコード前の単一文字列行の長さ |
maxStreamChunks | 10000 | デコードされたReadableStream、AsyncIterable、Iteratorのチャンク数 |
各上限は独立しています。1つを上書きしても、他の上限がデフォルトにリセットされることはありません。
react-server.config.mjsのserverFunctions.limits配下で上限を設定します:
react-server.config.mjsexport default {
serverFunctions: {
limits: {
// 公開された変更系エンドポイントに適した厳しい上限:
maxBytes: 1 * 1024 * 1024, // 1 MiB
maxDepth: 32,
maxBoundArgs: 16,
maxStreamChunks: 1000,
},
},
};
省略したフィールドはデフォルト値が保持されるため、上書きしたい上限のみを指定すれば十分です。
大きなペイロードを必要としない公開アプリでは、バイト上限と深度を大幅に下げます:
react-server.config.mjsexport default {
serverFunctions: {
limits: {
maxBytes: 256 * 1024, // 256 KiB
maxStringLength: 64 * 1024,
maxRows: 1000,
maxDepth: 32,
},
},
};
大きなファイルやストリームを正当にアップロードする内部ツールの場合:
react-server.config.mjsexport default {
serverFunctions: {
limits: {
maxBytes: 256 * 1024 * 1024, // 256 MiB
maxStreamChunks: 100_000,
},
},
};
上限を緩めることは、安全性を容量と引き換えにすることを意味します。インターネットに公開されているエンドポイントで上限を引き上げる場合は、リバースプロキシ層でのレート制限、認証、リクエストサイズ制限と組み合わせてください。
これらの上限は、デコーダの構造的防御(常に有効で設定不可)と連携して機能します:
- プロトタイプ汚染対策 —
__proto__、constructor、prototypeキーは、デコードされたオブジェクトの自身のプロパティになる前に、JSON解析中に除去されます。 - パスウォークのバリア — アウトラインされた行参照(
$<hex>:key:key)は、自身のプロパティ参照を介して、プレーンなObject.prototype/Array.prototypeの値のみを通過します。これにより、攻撃者が.constructor、.thenなどのガジェットを連鎖させることを防ぎます。 - Thenableのスクラブ — 値が関数である
thenキーはnullに置き換えられ、敵対的なペイロードが下流のPromiseコードによってダックタイプされる呼び出し可能なthenableを密輸できないようにします。 - 能力に紐付いた呼び出し可能オブジェクト — 関数値は、サーバリファレンスの許可リスト(
$h)または不透明な一時参照プロキシ($T)からのみ生成できます。デコーダはユーザーデータに対してeval、new Function、import()を呼び出すことはありません。
このドキュメントで設定可能な上限は、これらの構造的防御に加えて、別のリソース制限層を追加します。両者により、攻撃者は次のことができないことが保証されます:
- 細工されたリファレンスを介してプロトタイプを汚染したり、禁止されたプロパティパスに到達したりすること。
- サーバ関数の引数に呼び出し可能オブジェクトを密輸すること。
- 不正なリプライを送信するだけで、サーバに無制限の作業(CPU、メモリ、スタック)を強制すること。
上限を超えると、デコーダはDecodeLimitErrorをスローします。他のサーバ関数エラーと同様に、RSCのエラーパスを通じてクライアントに伝達されます。エラーバウンダリでキャッチして表示することも、カスタムHTTPミドルウェア内で検査してログ記録、アラート、または攻撃元のブロックを行うこともできます。
エラーには以下が含まれます:
code: "DECODE_LIMIT"limit— 超過した上限の名前(例:"maxBytes")observed— 拒否を引き起こした観測値
サーバ関数が実行される前に拒否されるため、上限を超えても拒否されたリクエスト自体以外には副作用がありません。