ブログ | NGINX

NGINX における QUIC ネットワークと暗号化の入門

NGINX-F5 水平黒タイプ RGB の一部
ロバート・ヘインズ サムネイル
ロバート・ヘインズ
2023年4月19日公開

NGINX ブログで QUIC と HTTP/3 が初めて言及されたのは4 年前(!) のことですが、皆さんと同じように私たちも、QUIC 実装が NGINX オープンソース メインライン ブランチに間もなく統合されることを心待ちにしています。 長い構想期間を考えると、QUIC についてあまり考えていないのも無理はありません。

ただし、この時点で、開発者またはサイト管理者は、QUIC がネットワークの詳細の一部に関する責任をオペレーティング システムから NGINX (およびすべての HTTP アプリ) に移行する方法について認識しておく必要があります。 ネットワークが得意ではない場合でも、QUIC を導入すれば、ネットワークについて心配することが (少なくとも少しは) 仕事の一部になります。

この記事では、QUIC で使用される主要なネットワークと暗号化の概念について詳しく説明します。わかりやすくするために、一部の詳細は簡略化し、重要でない情報は省略しています。 このプロセスで多少のニュアンスは失われるかもしれませんが、私たちの目的は、QUIC を効果的に環境に導入するのに十分な情報、または少なくとも知識を構築するための基盤を提供することです。

QUIC が初めての方は、まず以前の投稿を読んで、概要ビデオを視聴することをお勧めします。

QUIC のより詳細で完全な説明については、IETC QUIC ワーキング グループの優れたドキュメント「QUIC トランスポート プロトコルの管理性」と、このドキュメント全体にリンクされている追加資料を参照することをお勧めします。

QUIC のネットワークと暗号化に注意する必要があるのはなぜですか?

これまでのところ、クライアントと NGINX 間のネットワーク接続の厄介な詳細は、ほとんどのユーザーにとって特に重要ではありませんでした。 結局のところ、HTTP/ 1.xおよび HTTP/2 では、オペレーティング システムがクライアントと NGINX 間の伝送制御プロトコル (TCP) 接続のセットアップを処理します。NGINX は、接続が確立されると、それをそのまま使用します。

ただし、QUIC では、接続の作成、検証、管理の責任が基盤となるオペレーティング システムから NGINX に移行します。NGINX は、確立された TCP 接続を受信する代わりに、ユーザー データグラム プロトコル (UDP) データグラムのストリームを取得し、それをクライアント接続とストリームに解析する必要があります。 NGINX は、パケット損失、接続の再開、輻輳制御の処理も担当するようになりました。

さらに、QUIC は接続の開始、バージョンネゴシエーション、暗号化キーの交換を単一の接続確立操作に統合します。 また、TLS 暗号化は QUIC+HTTP/3 と TCP+HTTP/1+2 の両方でほぼ同様の方法で処理されますが、レイヤー 4 ロードバランサー、ファイアウォール、セキュリティアプライアンスなどのダウンストリームデバイスにとって重要な違いがある可能性があります。

最終的に、これらの変更の全体的な効果は、NGINX の構成や操作にほとんど変更を加えることなく、ユーザーにとってより安全で、より高速で、より信頼性の高いエクスペリエンスを実現することです。 ただし、NGINX 管理者は、問題が発生した場合に平均無害時間をできるだけ短くするためにも、QUIC と NGINX で何が起こっているかを少なくとも少しは理解する必要があります。

(HTTP/3 には QUIC が必要であるため、この投稿では HTTP 操作に焦点を当てていますが、QUIC は他のプロトコルにも使用できることに注意してください。 良い例としては、 RFC 9250DNS over Dedicated QUIC Connections )で定義されている DNS over QUIC があります。

前置きはここまでにして、QUIC ネットワークの詳細について詳しく説明しましょう。

TCP と UDP

QUIC は、クライアントとサーバー間で HTTP アプリケーション データを送信するために使用される基盤となるネットワーク プロトコルに大きな変更を導入します。

前述のように、TCP は常に HTTP Web アプリケーション データを送信するためのプロトコルでした。 TCP は、IP ネットワーク経由でデータを確実に配信するように設計されています。 接続を確立し、データの受信を確認するための明確に定義され理解されているメカニズムに加え、信頼性の低い混雑したネットワークでよく見られるパケット損失や遅延を管理するためのさまざまなアルゴリズムと手法を備えています。

TCP は信頼性の高いトランスポートを提供しますが、パフォーマンスとレイテンシの点でトレードオフがあります。 さらに、データ暗号化は TCP に組み込まれていないため、別途実装する必要があります。 また、HTTP トラフィック パターンの変化に応じて TCP を改善または拡張することも困難でした。TCP 処理は Linux カーネルで実行されるため、システム全体のパフォーマンスと安定性に予期しない影響が生じないように、変更は慎重に設計およびテストする必要があります。

もう 1 つの問題は、多くのシナリオで、クライアントとサーバー間の HTTP トラフィックが、ファイアウォールやロード バランサー (総称して「ミドルボックス」と呼ばれる) などの複数の TCP 処理デバイスを通過するため、TCP 標準の変更の実装が遅くなる可能性があることです。

QUIC は代わりに UDP をトランスポート プロトコルとして使用します。 UDP は TCP のように IP ネットワーク経由でデータを送信するように設計されていますが、接続の確立と信頼性の高い配信を意図的に排除しています。 このオーバーヘッドのなさにより、UDP は信頼性よりも効率性と速度が重要となる多くのアプリケーションに適しています。

ただし、ほとんどの Web アプリケーションでは、信頼性の高いデータ配信が不可欠です。 基盤となる UDP トランスポート層では信頼性の高いデータ配信が提供されないため、これらの機能は QUIC (またはアプリケーション自体) によって提供される必要があります。 幸いなことに、この点では QUIC には TCP に比べていくつかの利点があります。

  • QUIC 処理は Linux ユーザー空間で実行されるため、特定の操作に関する問題がシステム全体に及ぼすリスクは少なくなります。 これにより、新機能の迅速な開発がより実現可能になります。
  • 前述の「ミドルボックス」は、通常、UDP トラフィックの最小限の処理のみを実行するため、QUIC プロトコルの拡張を制限することはありません。

QUIC ネットワークの簡略化された構造

QUICストリームは、HTTP/3 リクエストまたはレスポンス (またはその他のアプリケーション データ) を含む論理オブジェクトです。 ネットワーク エンドポイント間の送信では、図に示すように、エンドポイントは複数の論理レイヤー内にラップされます。

図1. QUIC ストリームの構造

外側から内側に向かって、論理レイヤーとオブジェクトは次のようになります。

  • UDP データグラム– 送信元ポートと宛先ポート (長さとチェックサム データを含む) を指定するヘッダーと、それに続く 1 つ以上の QUIC パケットが含まれます。 データグラムは、ネットワークを介してクライアントからサーバーに送信される情報の単位です。
  • QUIC パケット– 1 つの QUIC ヘッダーと 1 つ以上の QUIC フレームが含まれます。
  • QUIC ヘッダー– パケットに関するメタデータが含まれます。 ヘッダーには 2 つの種類があります。

    • 接続の確立時に使用される長いヘッダー。
    • 接続が確立された後に使用される短いヘッダー。 これには、接続 ID、パケット番号、キー フェーズ (キー ローテーションをサポートするために、パケットの暗号化に使用されたキーを追跡するために使用される) などのデータが含まれます。 パケット番号は、特定の接続とキー フェーズごとに一意であり (常に増加します)。
  • フレーム– タイプ、ストリーム ID、オフセット、ストリーム データが含まれます。 ストリーム データは複数のフレームに分散されていますが、接続 ID、ストリーム ID、およびオフセットを使用して組み立てることができ、これを使用してデータ チャンクを正しい順序で表示できます。
  • ストリーム– 単一の QUIC 接続内での一方向または双方向のデータ フロー。 各 QUIC 接続は、それぞれ独自のストリーム ID を持つ複数の独立したストリームをサポートできます。一部のストリームを含む QUIC パケットが失われた場合でも、失われたパケットに含まれていないストリームの進行には影響しません (これは、HTTP/2 で発生するヘッドオブラインブロッキングを回避するために重要です)。 ストリームは双方向であり、どちらのエンドポイントでも作成できます。

接続の確立

よく知られているSYN / SYN-ACK / ACK の3 ウェイ ハンドシェイクによって TCP 接続が確立されます。

TCP 接続を確立するためのハンドシェイクでクライアントとサーバーの間で交換される 3 つのメッセージを示す図
図2. TCP接続を確立する3ウェイハンドシェイク

QUIC 接続を確立するには同様の手順が必要ですが、より効率的です。 また、暗号化ハンドシェイクの一部として、接続セットアップにアドレス検証を組み込みます。 アドレス検証は、悪意のある人物が、攻撃対象の偽装された送信元アドレス情報を含むパケットをサーバーに送信するトラフィック増幅攻撃から保護します。 攻撃者は、サーバーが攻撃者自身で生成できるパケットよりも多くのパケットまたは大きなパケットを被害者に生成し、その結果、膨大な量のトラフィックが発生することを期待します。 (詳細については、RFC 9000のセクション8 「QUIC」を参照してください。 UDP ベースの多重化された安全なトランスポート

接続確立の一環として、クライアントとサーバーは QUIC ヘッダーにエンコードされた独立した接続 ID を提供し、クライアントのソース IP アドレスとは関係なく、接続を簡単に識別できるようにします。

ただし、QUIC 接続の最初の確立には TLS 暗号化キーの交換操作も含まれるため、TCP 接続の確立中に生成される単純なSYN-ACK応答よりもサーバーの計算コストが高くなります。 また、キー交換操作が行われる前にクライアントの IP アドレスが検証されないため、分散型サービス拒否 (DDoS)攻撃の潜在的なベクトルも作成されます。

ただし、 quic_retryディレクティブをonに設定することで、複雑な暗号化操作を開始する前にクライアント IP アドレスを検証するように NGINX を設定できます。 この場合、NGINX はトークンを含む再試行パケットをクライアントに送信します。クライアントは、このトークンを接続セットアップ パケットに含める必要があります。

リプレイパケットの有無にかかわらず、QUIC 接続を確立するためのハンドシェイクを示す図
図3. QUIC 接続のセットアップ (再試行パケットありとなし)

このメカニズムは、3 ウェイ TCP ハンドシェイクに似ており、重要な点として、クライアントが提示する送信元 IP アドレスを所有していることを確立します。 このチェックが実施されていない場合、NGINX などの QUIC サーバーは、偽装された送信元 IP アドレスによる簡単な DoS 攻撃に対して脆弱になる可能性があります。 (このような攻撃を軽減するもう 1 つの QUIC メカニズムは、すべての初期接続パケットを最低 1200 バイトにパディングする必要があるという要件であり、これによりパケットの送信がよりコストのかかる操作になります。)

さらに、再試行パケットは、クライアントに送信する接続 ID に接続の詳細をエンコードすることで、TCP SYNフラッド攻撃 (メモリに保存された大量の開かれたが完了していないハンドシェイクによってサーバー リソースが枯渇する) に似た攻撃を軽減します。これにより、接続情報はクライアントによって後で提示される接続 ID とトークンから再構成できるため、サーバー側の情報を保持する必要がないというさらなる利点もあります。 この手法は TCP SYNクッキーに類似しています。 さらに、NGINX などの QUIC サーバーは、クライアントからの将来の接続で使用される有効期限のあるトークンを提供して、接続の再開を高速化できます。

接続 ID を使用すると、接続を基盤となるトランスポート層から独立させることができるため、ネットワークの変更によって接続が切断されることがなくなります。 これについては、 「クライアント IP アドレスの変更を適切に管理する」で説明されています。

損失検出

接続が確立されると(そして、以下でさらに説明するように暗号化が有効になると)、HTTP リクエストとレスポンスがクライアントと NGINX 間でやり取りできるようになります。UDP データグラムが送受信されます。 ただし、これらのデータグラムの一部が失われたり遅延したりする原因となる要因は多数あります。

TCP には、パケット配信の確認、パケット損失または遅延の検出、失われたパケットの再送信の管理を行う複雑なメカニズムがあり、適切に順序付けられた完全なデータをアプリケーション層に配信します。 UDP にはこの機能がないため、輻輳制御と損失検出は QUIC 層で実装されます。

  • クライアントとサーバーは両方とも、受信した QUIC パケットごとに明示的な確認応答を送信します (ただし、低優先度のフレームのみを含むパケットはすぐには確認応答されません)。
  • 信頼性の高い配信を必要とするフレームを含むパケットが、設定されたタイムアウト期間後に確認応答されない場合、そのパケットは失われたとみなされます。

    タイムアウト期間はパケットの内容によって異なります。たとえば、暗号化を確立して接続を設定するために必要なパケットの場合、QUIC ハンドシェイクのパフォーマンスに不可欠であるため、タイムアウトは短くなります。

  • パケットが失われたと判断された場合、失われたフレームは新しいシーケンス番号を持つ新しいパケットで再送信されます。
  • パケットの受信者は、パケット上のストリーム ID とオフセットを使用して、送信されたデータを正しい順序で組み立てます。 パケット番号は送信順序のみを指示し、パケットの組み立て方法は指示しません。
  • 受信側でのデータ アセンブリは送信順序とは無関係であるため、パケットの損失または遅延は、接続内のすべてのストリームではなく、パケットに含まれる個々のストリームにのみ影響します。 これにより、ストリームがトランスポート層の一部ではないため、HTTP/ 1.xおよび HTTP/2 に影響するヘッドオブラインブロッキングの問題が解消されます。

損失検出の完全な説明は、この入門書の範囲を超えています。 タイムアウトを決定するメカニズムと、転送中に許可される未確認データの量の詳細については、 RFC 9002QUIC 損失検出および輻輳制御を参照してください。

クライアントの IP アドレス変更を適切に管理する

クライアントの IP アドレス (アプリケーション セッションのコンテキストではソース IP アドレスと呼ばれます) は、セッション中に変更されることがあります。たとえば、VPN またはゲートウェイがパブリック アドレスを変更した場合や、スマートフォン ユーザーが WiFi がカバーする場所を離れ、セルラー ネットワークに強制的に切り替えられた場合などです。 また、ネットワーク管理者は従来、TCP 接続よりも UDP トラフィックのタイムアウトを低く設定しており、その結果、ネットワーク アドレス変換(NAT) の再バインドが発生する可能性が高くなります。

QUIC は、結果として生じる可能性のある混乱を軽減するための 2 つのメカニズムを提供します。クライアントは、アドレスが変更されることをサーバーに事前に通知でき、サーバーはクライアントのアドレスの予期しない変更を適切に処理できます。 接続 ID は移行中も一貫しているため、確認応答されていないフレームは新しい IP アドレスに再送信できます。

QUIC セッション中にソース IP アドレスが変更されると、ソース IP アドレスとポートを使用して特定の UDP データグラムを受信するアップストリーム サーバーを決定するダウンストリーム ロード バランサー (またはその他のレイヤー 4 ネットワーク コンポーネント) に問題が発生する可能性があります。 正しいトラフィック管理を確実に行うために、レイヤー 4 ネットワーク デバイスのプロバイダーは、QUIC 接続 ID を処理できるようにデバイスを更新する必要があります。 負荷分散と QUIC の将来について詳しくは、IETF ドラフトQUIC-LB を参照してください。 ルーティング可能な QUIC 接続 ID を生成します

暗号化

「接続の確立」では、最初の QUIC ハンドシェイクは単に接続を確立するだけではないという事実について触れました。 TCP の TLS ハンドシェイクとは異なり、UDP では、キーと TLS 1.3 暗号化パラメータの交換が初期接続の一部として行われます。 この機能により、複数の交換が削除され、クライアントが以前の接続を再開するときにゼロ ラウンドトリップ時間 (0-RTT) が有効になります。

図4. TCP+TLS/1.3 と QUIC の暗号化ハンドシェイクの比較

暗号化ハンドシェイクを接続確立プロセスに組み込むことに加えて、QUIC は TCP + TLS よりも多くのメタデータを暗号化します。 キー交換が行われる前であっても、初期接続パケットは暗号化されます。盗聴者はキーを導き出すことはできますが、暗号化されていないパケットの場合よりも手間がかかります。 これにより、攻撃者と潜在的な国家レベルの検閲の両方に関連するサーバー名インジケーター (SNI) などのデータがより適切に保護されます。 図 5 は、QUIC が TCP + TLS よりも機密性の高い可能性のあるメタデータ (赤で表示) を暗号化する方法を示しています。

図5. QUICはTCP+TLSよりも機密性の高いメタデータを暗号化します

QUIC ペイロード内のすべてのデータは TLS 1.3 を使用して暗号化されます。 利点は 2 つあります。古くて脆弱な暗号スイートとハッシュ アルゴリズムが許可されず、前方秘匿性 (FS) 鍵交換メカニズムが必須になることです。 前方秘匿性により、攻撃者が秘密鍵とトラフィックのコピーを入手したとしても、データを復号化できなくなります。

低RTTおよびゼロRTT接続により遅延が短縮

アプリケーション データが送信される前にクライアントとサーバー間で発生する必要のあるラウンド トリップの数を減らすと、特に待ち時間が長いネットワーク上でアプリケーションのパフォーマンスが向上します。

TLS 1.3 では、暗号化された接続を確立するために 1 回のラウンド トリップが導入され、接続を再開するために 0 回のラウンド トリップが導入されましたが、TCP では、TLS Client Hello の前にハンドシェイクを実行する必要があります。

QUIC は暗号化操作と接続セットアップを組み合わせるため、クライアントが最初の QUIC パケットでリクエストを送信できる真の 0-RTT 接続再確立を実現します。 これにより、最初のリクエストの前に接続を確立するための最初のラウンドトリップが排除され、レイテンシが短縮されます。

図6. TCP+TLSとQUICで接続を再確立するために必要なメッセージの比較

この場合、クライアントは以前の接続で使用されたパラメータで暗号化された HTTP リクエストを送信し、アドレス検証の目的で、以前の接続中にサーバーから提供されたトークンを含めます。

残念ながら、0-RTT 接続再開では Forward Secrecy が提供されないため、最初のクライアント要求は交換内の他のトラフィックほど安全に暗号化されません。 最初のリクエスト以降のリクエストと応答は、Forward Secrecy によって保護されます。 さらに問題となるのは、最初のリクエストもリプレイ攻撃に対して脆弱であり、攻撃者が最初のリクエストをキャプチャしてサーバーに複数回リプレイできる点です。

多くのアプリケーションや Web サイトでは、0-RTT 接続再開によるパフォーマンスの向上がこれらの潜在的な脆弱性を上回りますが、それは自分で決定する必要があります。

この機能は、NGINX ではデフォルトで無効になっています。有効にするには、 ssl_early_dataディレクティブをonに設定します。

Alt-Svcヘッダーを使用して HTTP/1.1 から HTTP/3 に移行する

ほぼすべてのクライアント (特にブラウザ) は、TCP/TLS 経由で初期接続を行います。 サーバーが QUIC+HTTP/3 をサポートしている場合、 Alt-Svcヘッダーにh3パラメータを含む HTTP/1.1 応答を返すことで、その事実をクライアントに通知します。 次に、クライアントは QUIC+HTTP/3 を使用するか、以前のバージョンの HTTP を使い続けるかを選択します。 (興味深いことに、 RFC 7838で定義されているAlt-Svcヘッダーは QUIC より古いものであり、他の目的にも使用できます。)

図7. Alt-Svcヘッダーを使用して接続を HTTP/1.1 から HTTP/3 に変換する方法

Alt-Svcヘッダーは、同じサービスが代替ホスト、プロトコル、またはポート (あるいはそれらの組み合わせ) で利用可能であることをクライアントに伝えます。 さらに、このサービスがどのくらいの期間、引き続き利用可能であると想定できるかをクライアントに通知することもできます。

例:

Alt-Svc: h3=":443" このサーバーのポート443ではHTTP/3が利用可能です。
Alt-Svc: h3="new.example.com:8443" HTTP/3 はサーバーnew.example.comのポート 8443 で利用可能です。
Alt-Svc: h3=":8443"; ma=600 HTTP/3 はこのサーバーのポート 8443 で利用可能であり、少なくとも 10 分間は利用可能になります。

必須ではありませんが、ほとんどの場合、サーバーは TCP + TLS と同じポートで QUIC 接続に応答するように構成されます。

Alt-Svcヘッダーを含めるように NGINX を構成するには、 add_headerディレクティブを使用します。 この例では、 $server_port変数は、NGINX がクライアントが TCP+TLS 要求を送信したポートで QUIC 接続を受け入れることを意味し、86,400 は 24 時間です。

add_header Alt-Svc 'h3=":$server_port"; ma=86400';

結論

このブログでは、QUIC の簡単な入門書を提供しており、QUIC で使用される主要なネットワークおよび暗号化操作を理解するのに十分な概要が提供されることを願っています。

QUIC + HTTP/3 用の NGINX の構成に関するより包括的な情報については、弊社のブログの「プレビュー NGINX QUIC+HTTP/3 実装用のバイナリ パッケージが利用可能になりました」をお読みいただくか、弊社のウェビナー「NGINX と QUIC+HTTP/3 を実際に使ってみる」をご覧ください。 QUIC+HTTP/3 のすべての NGINX ディレクティブの詳細と、ビルド済みバイナリのインストールまたはソースからのビルドの完全な手順については、 NGINX QUIC ウェブページを参照してください。


「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"