# サーバ関数の上限

`@lazarv/react-server`は、すべての受信RSCリプライ（サーバ関数呼び出し）に対して、サーバ関数のコードが実行される前にリソース上限を強制します。これらの上限はペイロードのデシリアライズコストを制限し、深くネストされた値、巨大な文字列、巨大なBigInt、無制限のストリームなど、リッチなRSCリプライワイヤフォーマットを悪用するDoS攻撃からサーバを保護します。

デフォルト値は、CVE後の強化（CVE-2025-55182および関連する強化）でReact本体に導入された上限と整合しています。多くのアプリケーションでは変更する必要はありません。これらの設定は、リスクの高い公開エンドポイントで上限をより**厳しく**したい場合や、正当に大きなサーバ関数ペイロード（ファイルアップロード、バッチ更新、大きなバインド引数配列など）を許可するために**緩める**必要がある場合に役立ちます。

## 仕組み

リプライデコーダはサーバ関数呼び出しを処理する最初のステップとして実行されます。ワイヤペイロード（JSON文字列または`multipart/form-data`の`FormData`）を解析し、以下の上限を強制してから、解決済みのサーバ関数に呼び出しをディスパッチします。

リクエストがいずれかの上限を超えると、デコーダは`DecodeLimitError`をスローし、標準のサーバ関数エラーパスを通じてクライアントに伝播されます。サーバ関数のコードは呼び出されません。エラーには超過した上限の名前と観測値が含まれており、運用上のトリアージが容易になります。

上限はデコーダ内で強制されるため、以下の両方をカバーします：

- **フォーム送信**（プログレッシブエンハンスメント、``、`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`のチャンク数 |

これらの上限はデコード *中* に実行されます。生のリクエストボディに対するワイヤレベルの上限は HTTP サーバ設定側で別途強制されます。デコーダレベルのチェックが実行される前にメモリ DoS を防ぐパース前の上限については、[`server.maxBodyBytes`](/features/http-layer#body-size-cap) を参照してください。

各上限は独立しています。1つを上書きしても、他の上限がデフォルトにリセットされることはありません。

## 設定

`react-server.config.mjs`の`serverFunctions.limits`配下で上限を設定します：

```mjs filename="react-server.config.mjs"
export default {
  serverFunctions: {
    limits: {
      // 公開された変更系エンドポイントに適した厳しい上限：
      maxBytes: 1 * 1024 * 1024,   // 1 MiB
      maxDepth: 32,
      maxBoundArgs: 16,
      maxStreamChunks: 1000,
    },
  },
};
```

省略したフィールドはデフォルト値が保持されるため、上書きしたい上限のみを指定すれば十分です。

### 上限を厳しくする

大きなペイロードを必要としない公開アプリでは、バイト上限と深度を大幅に下げます：

```mjs filename="react-server.config.mjs"
export default {
  serverFunctions: {
    limits: {
      maxBytes: 256 * 1024,   // 256 KiB
      maxStringLength: 64 * 1024,
      maxRows: 1000,
      maxDepth: 32,
    },
  },
};
```

### 上限を緩める

大きなファイルやストリームを正当にアップロードする内部ツールの場合：

```mjs filename="react-server.config.mjs"
export default {
  serverFunctions: {
    limits: {
      maxBytes: 256 * 1024 * 1024,    // 256 MiB
      maxStreamChunks: 100_000,
    },
  },
};
```

> 上限を緩めることは、安全性を容量と引き換えにすることを意味します。インターネットに公開されているエンドポイントで上限を引き上げる場合は、リバースプロキシ層でのレート制限、認証、リクエストサイズ制限と組み合わせてください。

## セキュリティモデル

これらの上限は、デコーダの構造的防御（常に有効で設定不可）と連携して機能します：

- **プロトタイプ汚染対策** — `__proto__`、`constructor`、`prototype`キーは、デコードされたオブジェクトの自身のプロパティになる前に、JSON解析中に除去されます。
- **パスウォークのバリア** — アウトラインされた行参照（`$:key:key`）は、自身のプロパティ参照を介して、プレーンな`Object.prototype` / `Array.prototype`の値のみを通過します。これにより、攻撃者が`.constructor`、`.then`などのガジェットを連鎖させることを防ぎます。
- **Thenableのスクラブ** — 値が関数である`then`キーは`null`に置き換えられ、敵対的なペイロードが下流の`Promise`コードによってダックタイプされる呼び出し可能なthenableを密輸できないようにします。
- **能力に紐付いた呼び出し可能オブジェクト** — 関数値は、サーバリファレンスの許可リスト（`$h`）または不透明な一時参照プロキシ（`$T`）からのみ生成できます。デコーダはユーザーデータに対して`eval`、`new Function`、`import()`を呼び出すことはありません。

このドキュメントで設定可能な上限は、これらの構造的防御に加えて、別の**リソース制限**層を追加します。両者により、攻撃者は次のことができないことが保証されます：

1. 細工されたリファレンスを介してプロトタイプを汚染したり、禁止されたプロパティパスに到達したりすること。
2. サーバ関数の引数に呼び出し可能オブジェクトを密輸すること。
3. 不正なリプライを送信するだけで、サーバに無制限の作業（CPU、メモリ、スタック）を強制すること。

## エラーハンドリング

上限を超えると、デコーダは`DecodeLimitError`をスローします。他のサーバ関数エラーと同様に、RSCのエラーパスを通じてクライアントに伝達されます。エラーバウンダリでキャッチして表示することも、カスタムHTTPミドルウェア内で検査してログ記録、アラート、または攻撃元のブロックを行うこともできます。

エラーには以下が含まれます：

- `code: "DECODE_LIMIT"`
- `limit` — 超過した上限の名前（例：`"maxBytes"`）
- `observed` — 拒否を引き起こした観測値

サーバ関数が実行される前に拒否されるため、上限を超えても拒否されたリクエスト自体以外には副作用がありません。