代表的な面接トピック

総合面接:Request.bytes() を使用してワンショットのボディを安全に読み取るにはどうすればよいですか?

一般普通
Offer.cc 編集チーム公開日 更新日

質問

ブラウザまたはエッジランタイムでバイナリのリクエストボディを処理する必要があります。プラットフォームには Request.bytes() が追加されますが、リクエストボディは一度しか消費できません。これを使用すべきタイミング、二重読み取りを防ぐ方法、メモリ上限を設定する方法、およびフォールバックの設計方法を説明してください。

プロンプトと範囲

ブラウザまたはエッジランタイムでバイナリのリクエストボディを処理する必要があります。プラットフォームには Request.bytes() が追加されますが、リクエストボディは一度しか消費できません。これを使用すべきタイミング、二重読み取りを防ぐ方法、メモリ上限を設定する方法、およびフォールバックの設計方法を説明してください。

MDN では、Request.bytes() はリクエストボディの Uint8Array を履行値(fulfilled value)とする Promise を返すと説明されています。ボディを読み取るとそれが消費されます。この面接では、新しいメソッドを万能なデフォルトとして扱うのではなく、Fetch ボディのライフタイム、リソース制限、およびエラーについてテストします。

面接官が評価するポイント

  • ボディ使用済み(used-body)状態と、bytes()arrayBuffer()json()、および text() の相互排他性についての説明。
  • リクエストサイズと処理目的に基づく、完全読み取りとストリーミング読み取りの選択。
  • リトライ、ロギング、署名検証、ビジネスパースの間における単一読み取り境界の配置。
  • 機能検出、制限、キャンセル、タイムアウト、およびエラーマッピングの設計。
  • 生のボディがログ、キャッシュ、またはテナント間オブジェクトに入り込むことの防止。

明確化のための質問

  • ボディのサイズ、ソース、Content-Type、署名要件、およびターゲットランタイムは何ですか?
  • ビジネス要件として、完全なバイト配列、段階的ハッシュ計算、チャンクアップロード、またはデコード済みデータのどれが必要ですか?
  • どのレイヤーがリトライを行い、リプレイは安全ですか?
  • ボディに個人データ、クレデンシャル、またはテナント分離データが含まれる可能性はありますか?

ボディのライフタイムと API の選択

Request.bytes() がボディを正常に消費した後は、以降の json()text() の呼び出しは失敗し、その逆も同様です。複数のコンシューマがデータを必要とする場合は、単一の境界で一度だけ読み取り、パース、検証、ビジネスコードに制御された結果を渡します。読み取りを競合する多数のモジュールに Request オブジェクトをそのまま渡してはなりません。

完全読み取りは、制限された小さなリクエストや、ワンショットでの検証が必要なプロトコルに適しています。大きなリクエストでは request.body によるストリーミングを優先し、段階的なハッシュ計算とサイズチェックを実行する必要があります。Uint8Array の結果はバイト処理に便利ですが、完全読み取りによるメモリピークを解消するものではありません。

メモリ、制限、およびバックプレッシャー

エッジで Content-Length を確認しますが、それだけを信用しないでください。チャンク転送の場合は実際のバイト数をカウントし、制限を超えた時点で中断(abort)します。完全読み取りに対してテナント単位、ルート単位、およびグローバルな上限を設定し、並行リクエストが無制限に配列を割り当てられないようにします。ストリーミングパスでは、コンシューマの処理が遅れた場合にバックプレッシャーを適用します。

転送のために、ロギングやリトライ目的で複数の完全配列をコピーしないでください。リプレイが必要な場合は、サイズ制限されたバイトをテナントバインディング、有効期限、ダイジェストとともに制御された一時ストレージに書き込みます。生のボディは通常のログに記録すべきではありません。

署名検証、パース、およびエラー

検証の前に、署名が生のバイトを対象とするのか、正規化された構造を対象とするのかを定義します。単一の読み取りから得られた生のバイトを、検証機能とパーサーの両方に渡します。JSON の再シリアライズは空白、順序、またはエンコーディングを変更する可能性があります。パースの失敗、署名の失敗、サイズ違反、キャンセル、タイムアウトを、単一の「不正なリクエスト(malformed)」レスポンスではなく、個別のビジネスエラーにマッピングします。

エッジ関数は異なるボディ API を公開している場合があります。機能を検出し、bytes()arrayBuffer()、またはストリーミングを選択し、起動時にランタイムの機能バージョンを記録します。フォールバックは、制限、ダイジェスト入力、およびエラーセマンティクスを維持しなければなりません。古いランタイムがセキュリティを暗黙的に弱めることがあってはなりません。

キャンセル、タイムアウト、およびリプレイ

読み取りおよびダウンストリームの操作に AbortSignal を渡します。クライアントが切断されたり期限が切れたりした場合は、読み取りを停止して参照を解放します。冪等性キー、サーバー側の重複排除、および明示的なリトライウィンドウがない限り、タイムアウト後に副作用を持つリクエストを自動的にリプレイしてはなりません。一見安全に見えるクエリであっても、リプレイによる漏洩リスクのレビューが必要です。

ゲートウェイのリトライによってリクエストのコピーが作成される可能性があるため、署名検証、冪等性キー、および監査イベントは単一のリクエスト ID を共有する必要があります。エラーレスポンスでは、内部の読み取り状態やキー情報を公開することなく、リトライが安全かどうかを示す必要があります。

セキュリティとオブザーバビリティ

Content-Type、ボディサイズ、読み取り時間、および並行性を制限します。伸長爆弾(decompression bomb)に対抗するため、展開後のサイズも考慮します。キャッシュや一時オブジェクトはテナントごとに分離し、機密バイトは必要な期間だけ保持します。バイト数、所要時間、キャンセルの理由、エラークラス、およびリクエストのダイジェストを記録し、コンテンツ自体は決して記録しないでください。

ボディ消費の失敗、制限違反、p95 読み取り時間、ピークメモリ、ダウンストリームのバックプレッシャー、およびリトライを監視します。特定のランタイムで bytes() の失敗が増加した場合は、テスト済みのフォールバックに切り替えてアラートを発報します。例外をキャッチして同じボディを再度読み取ろうとすることは、回復処理ではありません。

検証チェックリストとフォローアップ

空データ、1バイト、制限値付近、制限超過、チャンク転送、低速クライアント、切断、二重読み取り、署名不一致、伸長爆弾、並行リクエスト、古いランタイムのフォールバック、およびダウンストリームタイムアウトの各ケースをテストします。すべてのパスでボディが正確に一度だけ消費され、キャンセルによってそれ以降の読み取りが停止することを確認します。

json() を呼び出してから検証に bytes() を使用してはいけないのはなぜですか?

最初の読み取りによってボディはすでに消費されており、パースと再シリアライズによって空白、順序、またはエンコーディングが変更される可能性があるためです。生のバイトを一度だけ読み取り、その同じバイトを検証とパースの両方に渡してください。

ストリーミングよりも bytes() が好ましいのはどのような場合ですか?

プロトコルが完全なバイト検証を要求する、小さくサイズが制限されたリクエストに使用します。大容量ファイル、継続的なアップロード、および高い並行性には、ストリーミングと段階的処理が必要です。

フォールバックが同等に安全であることをどのように証明しますか?

各ランタイムで各機能パスを強制的に実行し、制限、ダイジェスト、署名、エラー、キャンセル、および監査イベントを比較します。フォールバックによってセキュリティポリシーやリトライ境界が変更されてはなりません。

公開情報ソース

関連する質問