代表的な面接トピック

バックエンド面接:楽観的なHTTP/1.1プロトコルアップグレードをどのように保護するか?

バックエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

プロキシがレイテンシを削減するために、HTTP/1.1のプロトコル切り替えが確認される前にアップグレード後のデータを送信したいと考えています。そのリスク、安全な境界、およびクライアントとプロキシで必要となる変更について説明してください。

プロンプトとスコープ

プロキシがレイテンシを削減するために、HTTP/1.1のプロトコル切り替えが確認される前にアップグレード後のデータを送信したいと考えています。そのリスク、安全な境界、およびクライアントとプロキシで必要となる変更について説明してください。

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

  • HTTP/1.1のアップグレードは確認前であれば拒否される可能性があり、過去の成功が保証にはならないことを理解しているか。
  • 拒否パスにおいて、攻撃者が制御するバイト列がどのようにHTTPリクエストとして再解釈され得るかを説明できるか。
  • Upgrade、CONNECT、WebSocket、およびHTTP/2/3の制約を区別できているか。
  • 2xxの待機、接続のクローズ、楽観的送信の無効化、観測可能なフォールバックなどのエンジニアリング制御を提示できるか。

明確化のための質問

  1. メカニズムはUpgradeとCONNECTのどちらで、対象となるターゲットプロトコルとHTTPバージョンは何ですか?
  2. 信頼できないアプリケーション、ユーザー、またはサードパーティオリジンが後続のバイト列を制御できますか?
  3. その接続はクライアント証明書、プロキシ認証、またはその他の接続レベルの信頼を保持していますか?
  4. 目的はハンドシェイクのレイテンシですか、それともHTTP/1.1の互換性と接続の再利用を維持する必要がありますか?

30秒の回答フレームワーク

私であれば、確認前におけるHTTP/1.1上の信頼できないアップグレード後データの送信は原則として禁止します。クライアントはアップグレード応答を待機します。CONNECTプロキシは2xxを待機するか、Connection: closeを送信して失敗後にクローズします。不明なプロトコルのバイト列を含む拒否された接続は再利用しません。マルチプレックスされたストリームセマンティクスについてはHTTP/2/3を個別に検証し、アップグレードとフォールバックの理由を記録し、制御されたプロキシ環境でリクエストスマグリングとパーサーの不一致をテストします。

ステップバイステップの詳細解説

1. 楽観的送信の前提を特定する

HTTP/1.1はUpgradeやCONNECTを使用してプロトコルを切り替えることができますが、サーバーがリクエストを拒否する可能性があります。クライアントがステータスを確認する前に新しいプロトコルのバイト列を送信した場合、2つのパーサーの可能性が生じます。受け入れられた場合は新しいプロトコル、拒否された場合はHTTP/1.1です。以前のアップグレードが成功したからといって、次も受け入れられる証明にはなりません。

2. リクエストスマグリングの経路を説明する

後続のデータが信頼できないソースによって制御されている場合、拒否パスによってそれらのバイト列が追加のHTTPリクエストとして解釈される可能性があります。接続レベルの認証がすでに成功している場合、プロキシは攻撃者が細工したリクエストを認証済みのクライアントトラフィックとして処理してしまう可能性があります。プロキシとクライアントの間でパース境界の不一致が生じることでも、パーサーの脆弱性が露見する可能性があります。

3. 安全な実装を選択する

最も安全なアプローチは、後続のデータを送信する前に確認を待つことです。RFC 9931では、HTTP/1.1 CONNECTプロキシクライアントに対して2xxを待機するか、Connection: closeを送信することを求めています。プロキシは安全でないCONNECTを拒否する際、基礎となる接続をクローズする必要があります。レイテンシが重要な場合は、明示的なストリームセマンティクスを持つHTTP/2以降を優先し、ターゲットプロトコル独自のハンドシェイク規則を検証してください。

4. フォールバック、監視、およびテストを構築する

アップグレード失敗時は、接続を再利用不可としてマークし、新しいHTTP/1.1リクエストを作成します。すでに送信された不明なバイト列を再試行可能なリクエストデータとして扱ってはいけません。認証情報やボディをログに記録することなく、受け入れ、拒否理由、クローズ、および再試行を測定します。信頼できないペイロード、認証済みプロキシ、401/407、リダイレクト、タイムアウト、パーサーの不一致、およびHTTP/2/3フォールバックをテストします。

質の高い模範解答

私であれば、楽観的送信を一般的な最適化手法としては扱いません。HTTP/1.1のUpgradeやCONNECTは確認前に拒否される可能性があり、攻撃者が制御する後続のバイト列が余分なリクエストとしてパースされ、リクエストスマグリングを引き起こす可能性があります。また、接続レベルの認証はその影響を増大させます。私はデフォルトで確認を待機するようにします。CONNECTプロキシは2xxを待機するか、Connection: closeを使用して拒否後にクローズします。Upgradeが失敗した接続は再利用しません。より低いレイテンシが必要な場合は、HTTP/2/3のストリームセマンティクスを評価します。信頼できないペイロード、認証済みプロキシ、エラーステータス、リダイレクト、再試行を用いて検証し、受け入れおよびクローズの理由を監視して、フォールバックによってリクエストや認証情報が決して漏洩しないようにします。

よくある間違い

  • 直近のアップグレードが成功したという理由だけで、任意の不特定な後続バイト列を送信してしまう。
  • サーバーのみを検証し、クライアント、プロキシ、およびサードパーティのパース境界を無視する。
  • 拒否されたCONNECT接続を再利用したり、バッファされたペイロードデータを転送したりする。
  • WebSocketのハンドシェイク規則をすべてのUpgradeトークンに適用してしまう。
  • HTTP/2/3のストリームセマンティクスを区別せずに、HTTP/1.1のみをベンチマークする。
  • 不明なバイト列を含む接続で再試行を行ったり、診断ログにボディを書き込んだりする。

フォローアップの質問と回答

なぜConnection: closeがリスクを軽減するのですか?

リクエストの処理後に接続をクローズするため、拒否されたアップグレードがその接続上で混在している可能性のある後続のバイト列のパースを継続しなくなるからです。これは接続の再利用性を犠牲にするため、2xxを待機することと比較検討する必要があります。

WebSocketは楽観的に送信できますか?

WebSocketのハンドシェイクでは、クライアントが後続のデータを送信する前にサーバーの応答を待機することが求められています。特定のプロトコルの規則をすべてのUpgradeトークンに一般化してはなりません。

レイテンシと安全性のバランスをどのように取りますか?

待機にかかる実際のコストを測定し、その上でHTTP/2またはHTTP/3を優先します。HTTP/1.1が必要な場合は、確認を待機し、フォールバック時には接続をクローズします。どのような最適化であっても、未確認のパース境界を信頼できないデータが越えることを許可してはなりません。

公開情報ソース

関連する質問