HTTP/2 ORIGINフレームのセキュリティを改善する提案仕様

AkamaiのMike Bishop氏らから、「DNS Security with HTTP/2 ORIGIN」という提案仕様が出ている。簡単に読む

ORIGINフレームとは

ORIGINフレームとは、RFC8336で標準化されているHTTP/2の拡張フレームです。

HTTP/2では、複数のドメインへのリクエストでもコネクションを使い回すことができます。https://a.example.comに接続した後に、https://b.example.comへのリクエストする場合、IPが同じで、そのドメインでも証明書が有効な場合(ワイルドカード証明書)は、すでにあるコネクションを再利用できます。

しかし、本当はa.example.comのサーバはb.example.comのコンテンツを提供できないという場合があります。その場合、サーバはステータスコード421を返し、それを受け取ったクライアントは改めてb.example.comにコネクションを張り直してコンテンツを取得します。これでは実際にコンテンツを取得するのが遅くなってしまいます。

ORIGINフレームでは、そのサーバがコンテンツを提供できるドメインリストをサーバから通知することで、クライアントが既存のコネクション上でリクエストを送信できるドメインを知ることが出来るようにします。

こうすることで、より効率よくコネクションの再利用が出来るようになります。

フレーム構造などは以前書いた記事が詳しいです
asnokaze.hatenablog.com

DNS Security with HTTP/2 ORIGIN

このとき、クライアントはORIGINフレームにかかれているドメインであれば無条件に既存のコネクションを使って良いわけではありません。サーバが悪意を持って他のドメインをORIGINフレームに記載すれば、本来のドメインを所持するサーバへ行くリクエストを、自身に送らせることができます。

そのため、通常のコネクションを再利用する時と同様、そのドメインがコネクションを張ったときに取得した証明書でも有効であることを確認します。その他にもCTログやOCSPを確認して証明書の有効性を確認することを推奨しています。

しかし、ORIGINフレームの仕様(RFC8336)では、ORIGINフレームで提供されたドメインの名前解決をすることは義務付けられていません。それは、名前解決の遅延や、新たな名前解決はクライアントが接続しようとしているサイトをネットワーク運用者に晒すことになるため、クライアントに一任されています。

しかし、名前解決が必須でないことは問題であり、必須にしようというのが「DNS Security with HTTP/2 ORIGIN」の提案です。

想定攻撃

この提案では名前解決を用いない場合にある種の攻撃が容易になるとしています。

たとえば秘密鍵が漏れた場合などを想定しています。そのときに、攻撃者はそのドメインへのリクエストを、自身のサーバに誘導することがきでるようになります。

例えば、victim-server.example.com秘密鍵が流出した場合。

攻撃者は、まずユーザに自身のWebサーバにアクセスさせます、そのURLなんでも良く、SNS掲示板に貼ればアクセスさせることは容易です。その後、攻撃者のサーバはORIGINフレームでvictim-server.example.comのコンテンツが提供できるとクライアントに通知し、さらに「Secondary Certificate Authentication in HTTP/2」の仕様に乗っ取りvictim-server.example.comの証明書と、秘密鍵の所持を証明します。

そうすると、クライアントはvictim-server.example.comへのリクエストを攻撃者のサーバに送信することになります。victim-server.example.comが、twitterだったりgoogleだったりすると思うと恐ろしいですね。

名前解決を行っていれば、この攻撃を防ぐことができます。もちろん、名前解決の通信を改ざんできれば攻撃は可能ですが、ORIGINフレームを用いることで攻撃が容易になっているということです。

おわりに

上記のような攻撃を緩和するために、ORIGINフレームで名前解決を必須にしようという提案でした。

不正な証明書が発行されたり、秘密鍵が漏れた場合に、CTログやOCSPに反映されるまでの間に発生しうる攻撃に対して、どれまでの緩和策が必要なのかというのは難しい問題だなと感じました。

2018年 振り返り (300記事目)

簡単に2018年を振り返ります。

2018年

今年も52記事を書くことができました。興味を持っていただけた記事も多く嬉しく思います。
また夢でもあった「単著の本」を出せたことも良かったと思います。

一方で、年末はあまり記事がかけてなかったのと、実際に手を動かして試したり全然出来なかったためにどうも反省点の残る年という印象が強いです。
昨年よりもここに書けることが少ないのは残念です。

300記事目

ふと現在の投稿数が299ですので、この記事が300投稿目になります。

ありがたい事に参考になったというコメントを頂くことがあります。やりたい事をやって人の役に立てるというのは幸せだなと思いました。

来年

来年もHTTP, QUIC, WICGらへんを中心に仕様を読み漁りたいと思います。実際に手も動かしていきたいところです。

まずは、次世代WebカンファレンスでHTTP/3のセッションがございます。よろしくおねがいします。
nextwebconf.connpass.com

願わくは来年も誰かの役に立てますように

記事一覧 (降順)

QUIC,HTTP/3 の draft-17に関するメモ

12月8日に現在標準化が進められているQUICの仕様のdraft-17が出ました。

以下の記事で書いたように、"HTTP over QUIC" のHTTP/3への名称が含まれています。
asnokaze.hatenablog.com

その他にも幾つかの変更が含まれているのでざっと目を通す。

この draft-17 は「9th Implementation Draft」であり、2019年1月に東京で開催されるQUIC WGの中間会議において draft-17対応実装を持ち寄って相互接続性試験が実施される予定です。

チャーターに書かれている通り、2019年7月に次のステップとなるIESGへの提出に向けて作業が続けられています。

spin bit

QUICではAckなどの制御情報も暗号化されます。経路上からはRTTやパケットロスの観測はできないようになっています。トラブルシュートの役に立てるために、経路上でRTTを推測可能にするSpin Bitという仕組みが長らく議論されてきました。

仕組みはいぜん書いたとおりです。
asnokaze.hatenablog.com

IETF 103 でも議論されました。この機能の実装意思についても実装者によって大きく別れました (議事録より)

最終的には拡張機能であり、コアの仕様ではショートパケットヘッダに予約ビットを確保はする形になりました。
draft-17ではビットの予約とSpin Bitの仕様である「The QUIC Latency Spin Bit」への参照が付きました

packet header

上記Spin Bitの予約のほか、ロングパケットヘッダ、ショートパケットヘッダの1バイト目について整理がされました。また今までのパケット番号保護から仕組みが変わって、ヘッダ保護(header protection)が付きました。これによって、パケットヘッダの特定のフィールド及び、パケット番号が暗号化されます。接続中は同じヘッダー保護キーが仕様されます。

ロングパケットでは1バイト目の下位4bit (予約領域と、パケット長)

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+
   |1|1|T T|R R|P P|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ...

   +-+-+-+-+-+-+-+-+
   |1|1|T T|E E E E|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Version -> Length Fields                 ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

ショートパケットでは1バイト目の下位5bit (予約領域と、鍵フェーズ、パケット長)

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+
   |0|1|S|R|R|K|P P|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   ....

   +-+-+-+-+-+-+-+-+
   |0|1|S|E E E E E|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |               Destination Connection ID (0/32..144)         ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

両方のパケットで、上記続くパケット番号もマスクされます。
TLSレイヤから得られたシークレットから導出するヘッダ保護鍵と、パケット保護を行いサンプリングされた値を入力として得られたビット列をマスク値として使用します。

off-path migration attacks対策

経路上でパケットを観測し、複製したパケットを本来のパケットよりも早く届けることで、アドレスが変わったと思わせることが出来る。これによって、以降のパケットが観測者の経路を通るようになる。PATH_CHALLENGEを使って攻撃を緩和

その他トランスポートレイヤの変更
  • クローズ後のアンプ攻撃対策。未検証アドレスへのデータ送信量の制限
  • PMTUを1280以下に変更しない

などなど

HTTP/3

"HTTP/QUIC" から "HTTP/3" に名称が変更されました。その他にも幾つかの機能お改善が入ってます

優先度処理

HTTP/2の優先度制御はクライアントとサーバで状態を同期する必要があります。HTTP/3ではもともとコントロールストリーム上でプライオリティを扱っていましたが、HoLBとなるのでdraft-17で変更されました。

各リクエストストリームの最初にPRIORITYフレームを送信可能になったほか、exclusiveフラグが廃止されました。

DUPLICATE_PUSHフレーム

HTTP/2とは異なりパケットロスによりフレームが失われるかもしれないので、同じリソースのPUSH_PROMISフレームを複数送れるようになっていましたが、明示的なDUPLICATE_PUSHフレームが定義されました。

もともとの同一のリソースの複数のPUSH_PROMISフレームでは、同じリクエストヘッダであることが必要でしたが、それは今まで送られてきたPUSH_PROMISEフレームを覚えておく必要があることと同義でした。

DUPLICATE_PUSHフレームではリクエストヘッダは記述せず、すでに送ったPUSH IDのみであるため、そのような間違いは怒らなくなっています。

0長DATAフレーム

ペイロード長が0のDATAフレームが許可されました。その場合は、明示的な長さを持たずデータが終わるまで受信できるようになります。

最初から長さがわかってない場合のデータを返す場合に使用されるものと思われます。

新しいユーザエージェント表現 User Agent Client Hints の提案仕様

ユーザエージェントは歴史的背景により、複雑な文字列が使用されています。

iOSでは

     User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X)
                 AppleWebKit/605.1.15 (KHTML, like Gecko)
                 CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1

Edgeでは

  User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
              AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.2704.79
              Safari/537.36 Edge/18.014

これには、不必要な情報や、歴史的な互換性を表現するための文字列などが含まれています。

ユーザエージェントを見て、適切なコンテンツを返すというのはこの複雑なユーザエージェントを正しく解釈する必要があります。

これを改善するためにClient Hintsの仕組みに則った新しい仕様である、「User Agent Client Hints」がGoogleのMike West氏から提案されています。

この提案では、ブランド名・プラットフォーム・アーキテクチャを個別に表現するヘッダを定義しています。

まずブラウザが https://example.comに最初にアクセスしたとき、Sec-CH-UAヘッダでブランド名とメジャーバージョンを含む情報を伝えます。

Sec-CH-UA: "Examplary Browser 73"

その後、サーバはさらなる情報が欲しい場合は、Client hintsの仕組みに乗っ取りAccept-CHレスポンスで欲しい情報を明示します。

Accept-CH: UA, Platform

サーバからのレスポンスを受け取ったブラウザは以後、次のヘッダを用いてユーザエージェント情報を伝えます。以下では、Sec-CH-Platformでプラットフォーム情報を送信しています。

     Sec-CH-UA: "Examplary Browser 73.3R8.2H.1"
     Sec-CH-Platform: "Windows 10"

新しく定義されるプロパティ

  • "brand" (for example: "cURL", "Edge", "The World's Best Web Browser")
  • "major version" (for example: "72", "3", or "28")
  • "full version" (for example: "72.0.3245.12", "3.14159", or "297.70E04154A")
  • "platform brand" (for example: "Windows NT", "iOS", or "AmazingOS")
  • "platform version" (for example: "10", "12", or "17G")
  • "platform architecture" (for example: "ARM64", or "ia32")
  • "model" (for example: "", or "Pixel 2 XL")

新しく定義されるヘッダ

各プロパティは下記のヘッダに含まれて送信されます。

  • Sec-CH-Arch
  • Sec-CH-Model
  • Sec-CH-Platform
  • Sec-CH-UA

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
            Chrome/71.1.2222.33 Safari/537.36
Sec-CH-UA: "Chrome 74.0.3424.124"
Sec-CH-UA-Platform: "macOS 12"
Sec-CH-UA-Arch: "ARM64"

ヘッダ先頭にSec-CH-が付く理由

W3C TAGのレビューに基づいています。
Sec-をつけることでJavaScriptからのアクセスを禁止し、CORSも不要となります。

github.com

HTTP over QUICと、その名称について (HTTP3について)

2018/12/23 追記
アップデートについて別記事書いた 「QUIC の draft-17に関するメモ - ASnoKaze blog

2018/11/08 追記
IETF103において、QUIC WGとHTTPbis WGそれぞれでHumが行われ
HTTP over QUICの名称をHTTP/3とするコンセンサスが得られました


目次


前回書いたとおり、現在IETFでQUICの標準化がされている
(QUICのトランスポートレイヤについては、下記記事参照)
asnokaze.hatenablog.com

今回はちょうどホットなHTTP over QUICの名称と、概略について雑に書く

HTTP over QUICの名称

Googleが現在使っているQUICとIETFで標準化を行っているQUICはとこどころ異なっており互換性はない。区別して前者をgQUIC、後者をiQUICと呼び分ける。(参照: gQUICのiQUICへの移行について)

実験的なプロトコルであったGoogleのQUICに対して、標準化されたQUICは別の名称をつけるという議論もありましたが、名前を変えずQUICとする方向になっています。

(GoogleのドキュメントではQUICを「Quick UDP Internet Connections」としていますが、IETFのドキュメントではQUICはQUICであり何かの略語ではないとしています)

名前のもう一つの混乱としては、IETFの"QUIC"はトランスポートプロトコルとしての名称であるが、HTTP over QUICを指していると思ってる人がいるところである。IETFで標準化しているQUICはトランスポートプロトコルであり、アプリケーションプロトコルはHTTPに限定されない。

このような混乱を避けるため、QUIC-the-HTTP-bindingに別名をつけQUICから明確に区別できるようにしないかという議論が出ています。

IETF QUIC WG チェアのMark氏より「Identifying our deliverables」というメールがMLに投稿されています。このメールではHTTP/3という名称が提案されています。

これは、現在開催中のIETF103でも議論される予定ですが、HTTP WGとQUIC WGでのコンセンサスが得られればその方向で進む可能性が強くなるでしょう

f:id:ASnoKaze:20181106020504p:plain
(IETF103のQUIC WGのスライドより)

なにかアップデートがあれば更新します。

HTTP over QUIC について

現在の仕様は「Hypertext Transfer Protocol (HTTP) over QUIC」こちら。

さてHTTP/3というと、なにか新しそうなHTTPのような気がしますが、まず基本的なHTTPのセマンティクスに変更はありません。GETやPOSTといったHTTPリクエストがあって、ステータスコードを持つレスポンスが返されます。ただその伝達方法がQUICトランスポートに合わせて改善されています。

個人的な印象としては、HTTP/2に対して下位レイヤとしてQUICトランスポートを使うための変更が入っている感じである。ただし、その変更は多くHTTP/2 over QUICではないという事がわかると思う。

ストリームとフレーム

QUICではトランスポートレイヤにストリームを持つため、HTTP/2であったストリーム管理の多くはなくなっています。例えば各ストリームのフロー制御はなくなっています。QUICレイヤで提供されるストリームをどのように使うかが、HTTP over QUICの仕様では定義されています。それに合わせてHTTP/2で使用していたフレームも一部なくなったり、新しく増えたフレームタイプもあります。それぞれのフレームのフォーマットも変更されています。

HTTP over QUICで定義されるフレームは以下の通り

  • DATA
  • HEADERS
  • PRIORITY
  • CANCEL_PUSH
  • SETTINGS
  • PUSH_PROMISE
  • GOAWAY
  • MAX_PUSH_ID
  • Reserved Frame Types

ストリームの扱いに関しては、通常のHTTPリクエスト/レスポンスは双方向ストリームを使用しますが、SETTINGSなどを送信する制御ストリームやサーバプッシュに使われるプッシュストリームなどは単方向ストリームを使用します。

また、サーバプッシュはPUSH IDと呼ばれる識別子で識別されます。このPUSH IDを用いて、パケットロスを見越して複数回同じプッシュを行うということができます。また、プッシュを中断するためのCANCEL_PUSHフレームや、プッシュ数を制限するMAX_PUSH_IDフレームなどが追加されています。

プライオリティ制御

HTTP over QUICでもHTTP/2のようなストリームに対してWeightとDependencyを設定することで優先度を設定します。

HTTP/2のプライオリティ制御では、ストリームは実際には使用せずプライオリティ制御のためだけに特定のストリームを使用できました。

例えば下記のようなHTTP/2 のdependency treeにおいて、ストリーム3,5,7は各リソースの優先度をグルーピングするためだけに使用されておりました。
f:id:ASnoKaze:20170728010502p:plain

しかし、QUICのストリームは各ストリームタイプでID順に使用していくことになっています。そのため、HTTP/2で行っていたような、実際にはデータの送受信には使用しないストリームをプライオリティ制御のグルーピングのために使用できません。

そのかわりに、HTTP over QUICではPlaceholderというそれ用のIDを用意しておき、そこに対して依存関係を指定するようになっています。また、ストリームID 0は今回はdependency treeのルートではないので明示的にルートしめす値が用意されています。

また、HTTP/2のときにあった問題として、特定のストリームがクローズしていても将来優先度処理で使用される可能性があるのでいつ状態(weight)を破棄していいか分からなかったという点です。HTTP over QUICでは非アクティブなストリームに関する、dependency treeの枝刈りに関する文書が加わり、クライアントは非アクティブなストリームに依存するようなストリームを作成すべきではなくなりました。

QPACK

HTTP/2ではHTTPヘッダ圧縮の仕組みとしてHPACKを使用していました。HTTP over QUICでは、各ストリームの順序が入れ替わる可能性があるため、クライアントが送信したHTTPリクエストの順番の通りにサーバが受信するとは限らなくなりました。この場合、HPACKの動的ヘッダテーブルの更新はクライアントとサーバで状態を正しく動悸することができません。これをそのまま正しく同期しようとおもうと、正しい順番に並び替えるための待ち時間が発生してしまいます。

そこで、HTTP over QUICではQPACKと呼ばれるヘッダ圧縮方式を使用します。この方式では、上記の問題が改善されています。

また、HTTP/2の静的ヘッダテーブルは当時のヘッダ利用状況に基づき作成されましたが、alt-svcヘッダの使用頻度が上がっているなど状況が異なっています。
https://github.com/quicwg/base-drafts/pull/1355 で示されるよう、静的ヘッダテーブルも大きく更新されています。

QUICサーバのディスカバリ

(alt-svcは HTTP over QUIC独自の仕組みではないが)

HTTP over QUICのポートは決められていません。下記の方法でサーバ側からポートも含め指定します。

HTTPレスポンでalt-svcヘッダを用いることで、このオリジンのページを別プロトコル(別サーバ)で提供できることをクライアントにアドバタイズします。
クライアントがHTTP over QUICに対応していればそこに接続してきます。

   Alt-Svc: hq=":50781"

ここで指定する識別子も、名称が変わった場合変更されます。

そのた

その他トランスポートレイヤの変更に伴い、下記の部分について変更が入っている

  • CONNECTメソッドによるトンネリングとエラーについて
  • エラーコード (まちがったストリームタイプの使用など)
  • コネクションの確立と切断

QUICの話 (QUICプロトコルの簡単なまとめ)

2018/12/23 追記
アップデートについて別記事書いた 「QUIC の draft-17に関するメモ - ASnoKaze blog

20181106追記 HTTP over QUICについてちょっと書きました
asnokaze.hatenablog.com

QUICの話

QUICのプロトコルについて、自分の理解を整理するためにその中身について簡単にまとめていきたい。

本当は体系的にちゃんとまとめていきたい。。。

(なお、draft16を元に書いている)

QUICの概要

QUICと今

プロトコルの中身に入る前に、簡単にQUICについて紹介をする。

QUICはもともとGoogleによって考案実装されたUDP上で動作するプロトコルであり、TCPのような信頼性を持ちTLSのような暗号化された通信路で効率的なHTTPのメッセージをやりとりする。すでにChromeGoogleのサービスで実装されている。そのデプロイについては以前このブログで触れた、GoogleのQUIC関係者によって書かれた論文が詳しい(こちらを参照)。

Googleでの実験をへて、2016年11月より正式にIETFでQUIC WGが結成され、QUICの標準化が進められている。

ここで注意すべきは、Googleが現在使っているQUICとIETFで標準化を行っているQUICはとこどころ異なっており互換性はない。区別して前者をgQUIC、後者をiQUICと呼び分ける。(参照: gQUICのiQUICへの移行について)

iQUICは実装はすでにいくつかあり、定期的に実装をもちよって相互接続性試験を行っており、その結果を仕様側にフィードバックし仕様の確度をあげている。今は「9th Implementation Draft」となっており、その相互接続性試験の結果はスプレッドシートにまとめられている(URL)。

なお、iQUICはトランスポートプロトコルであり、上位のプロトコルとしてまずHTTPを想定しているがHTTPのみには限定されない。
以後、特に断りがない場合、QUICと行った場合 iQUIC を指す。

背景とQUICの目的

Webの配信高速化のために、HTTP/1.1からHTTP/2への改定された。より高速化のために、トランスポートレイヤでの問題に焦点が当てられるようになりました。

例えば下記の点があげられる

  • TCPでは、パケットロスがあるとパケットロスが回復されるのをまってアプリケーションにデータが渡される。
    • HTTP/2では複数のリクエスト・レスポンスが一つのTCPコネクションに多重化されている。
    • HTTPレスポンスAとHTTPレスポンスBがあったとする、片方のデータのパケットロスが、もう片方の通信もブロックさせてしまう。
  • ハンドシェイクのラウンドトリップ回数 (TCP + TLS)
    • 通信を開始するにあたって、TCPTLSのコネクション確立にラウンドトリップタイムがかかる
  • クライアントのIP / ポート変更に伴うコネクションの切断
    • Wi-Fiからキャリア回線などにハンドオーバーした際、TCPコネクションが切れてしまう

そのような問題を解決するために、QUICでは下記の項目にフォーカスして標準化が進められました

  • コネクション確立と全体的な転送待ち時間を最小限に抑え、アプリケーションデータをより早いタイミングで転送可能とする
  • ヘッドラインブロッキング(HoLB)なしで多重化を提供する。パケットロスの影響を最小化する
  • エンドポイントのみが対応すればQUICが疎通するようにする(スイッチや中間装置の対応が不要)
  • マルチパスとFEC拡張の対応
  • デフォルトでTLS 1.3を使用して、常に安全なトランスポートを提供する。

フォーカス項目としては以上のとおりだが、QUICの機能は多く、上記にあがってないが、例えばECN(Explicit Congestion Notification)がサポートされていたりとたくさんの機能が組み込まれている。

ちなみに、現在はQUIC v1と呼ばれる機能の標準化を行っており、マルチパスやFECといった機能は先送りとなっている。しかし、そういった機能が将来的には対応できるよう拡張性をもっている。

QUICのオーバービュー

QUICのプロトコルスタックは以下の通りである
f:id:ASnoKaze:20181031000151p:plain
(引用: QUIC Tutorial)

左側のスタックが一般的に使用されているHTTP/2で、右側がQUICのスタックです。

QUICの構成としては

  • QUICで提供されるトランスポート上でHTTPのメッセージをやりとりする。
  • コネクション確立時にTLS1.3のメッセージをやり取りし、以後のメッセージを暗号化する鍵を取得する(0-RTT鍵, 1-RTT鍵)。
    • メッセージのAckやコネクションの切断メッセージといった制御メッセージも暗号化される。
  • UDP上で、TCPのような信頼性(パケロスの回復・パケットの順番の入れ替わりの補正)や輻輳制御フロー制御を提供するレイヤ。
    • パスの到達性や、アドレスの所持確認の機能や、反射攻撃を防ぐ工夫など
    • ストリームと呼ばれる仮想的な通信単位を持つ
    • 輻輳制御やパケロス検出アルゴリズムTCPなどで使用されているものをそのまま使用する


HTTP部分については、通常のHTTP/2のメッセージ形式を使うのではなくQUICで扱うための変更が入っている

  • 通所のHTTP/2に似ており、HTTPメッセージをやりとりする
  • 後述のストリームの管理がQUICレイヤに移り、それにあわせフレームの変更やQUICストリームの利用方法の定義
  • ヘッドオブラインブロッキング避けるために、HPACKをQUIC用に改良したQPACKを用いる
  • サーバプッシュやプライオリティの扱いが少し変更された
  • QUICにあわせてエラーメッセージの追加など

提案仕様はレイヤごとに別れており、スタックと合わせてみるとわかりやすい

プロトコルの話

ここからQUICのプロトコルの中の話に入っていく

QUICの用語

QUICで用いる用語について簡単に紹介する。

エンドポイント
  • クライアント: QUICコネクションを開始する側
  • サーバ: QUICコネクションを受け付ける側
  • エンドポイント: クライアントもしくはサーバ
  • ピア: 反対のエンドポイント
コネクションとパケット
  • QUICコネクション: UDP上のQUICレイヤのいわゆるコネクション。可変長整数のコネクションIDによって識別される
    • Source Connection ID: 送信元のコネクションID (一度、コネクションが確立すると省略される)
    • Destination Connection ID: 送信先のコネクションD
  • QUICパケット
    • ClientとServerによってやりとりされるデータの単位
    • UDPパケットの中にQUICパケットが結合されて格納される場合がある(ただし個別のQUICパケットとして処理される)
    • QUICパケットにタイプが有り、それぞれロングヘッダもしくはショートヘッダを持つ
    • ロングヘッダはコネクション確立時に利用され、ショートヘッダはそれ以降利用される
    • パケットタイプ毎にパケット番号を持ち、パケットを送るごとにインクリメントされる

f:id:ASnoKaze:20181031010348p:plain

ストリームとフレーム
  • フレーム
    • QUICパケットの中に含まれるメッセージ単位
    • 現在のQUICの仕様では21のフレームタイプが定義されており、フレームによって様々なメッセージを格納する。例えば次の通り
    • 暗号ハンドシェイクのデータ、アプリケーションプロトコルのデータ、Ackやクローズといった制御情報、フロー制御情報
    • パケットがロスした場合は、再送する必要のあるフレームのみ、新しいパケット番号を持つパケットで再送される
  • ストリーム
    • QUICはHTTP/2のように、コネクションの中にストリームと呼ばれる仮想的な通信単位を持つ
    • いくつかのフレームは、ストリームIDを持ちストリームと紐付けられる
    • 各ストリームはストリームIDを持つ
    • ストリームにはタイプが有り、そのストリームIDの下位2bitによって次の通り分かれる
      • 0x0:クライアントが作成した双方向ストリーム、0x1:サーバが作成した双方向ストリーム
      • 0x2:クライアントが作成した単方向ストリーム、0x3:サーバが作成した単方向ストリーム
    • 同じストリームに属するデータ内でのみ、パケットロスや並び替わりでブロックが発生する。他のストリームのデータはブロックしない。
    • (UDPなので、来たデータからユーザ空間で処理可能)

f:id:ASnoKaze:20181031005926p:plain
(STREAMフレームはHTTPなどのアプリケーションデータを運ぶ)

QUICのコネクション確立/切断

コネクションの確立

QUICのコネクションの確立は下記のとおり行われます。
f:id:ASnoKaze:20181031012530p:plain

このときにトランスポート用パラメータと、TLSハンドシェイクをあわせて行うため、RTTが小さくなっています

TLS1.3の鍵交換のメッセージはCRYPTOフレームに格納されますが、コネクションを確立するための最初のCRYPTOフレームはInitialパケットで送信されます。各パケットタイプ毎にAckを返し、続いてHandshakeパケットでCRYPTOフレームを送信しTLS1.3のハンドシェイクを完了します。こうして1-RTT鍵が取得できたら、1-RTT鍵で保護されたパケットでアプリケーションデータを送受信できるようになります。

明示的にクライアントのIPアドレス所持を確認する方法もありますが、このハンドシェイクを通してクライアントのIPアドレス所持を確認できます

初期ウィンドウサイズや、ストリームの上限といったトランスポートパラメータはQUIC TLSで定義されるquic_transport_parameters拡張に格納され、ハンドシェイク後にきちんと認証されます。

コネクションの切断

QUICのコネクションの切断は3種類ある

アイドルタイムアウトはその名の通り、トランスポートパラメータでアドバタイズされたアイドルタイムアウト値を超えた場合はクローズされる。なお、タイムアウト直前に送られたパケットが届く可能性があるため、draining状態になってから規定時間待ってコネクションの状態を破棄する。

即時切断は、CONNECTION_CLOSEフレームもしくはAPPLICATION_CLOSEフレームを送信することでコネクションを即時にクローズすることができます。なお、アイドルタイムアウトと同様、パケットの順番などが入れ替わる可能性があるのでdraining状態をヘてコネクションの状態が破棄されます。

ステートレスリセットは、コネクションを切断するための最後の手段です。仮にサーバ側の再起動などにより鍵情報が失われ、サーバが届いたパケットを正しく解読できなくなった場合、CONNECTION_CLOSEといった切断用のメッセージも解釈できなくなってしまいます。そうするとクライアントはアイドルタイムアウトまで待たないとコネクションが切断できなくなってしまいます。

上記の問題を防ぐために、サーバ側から事前にトークンを暗号路で発行しておき、そのトークンをステートレスリセットパケットでサーバに送ることでトークンと紐づくコネクションを切断できるようにします。

一度使ったトークンは二度としようできません。

QUICのフレーム

フーレムの概要

  • PADDING: 意味のないフレーム。パケットサイズを増加させたりするのに使用される。
  • RST_STREAM: ストリームを終了する
  • CONNECTION_CLOSE: コネクションをクローズする
  • APPLICATION_CLOSE: アプリケーションレイヤの都合によってコネクションをクローズする
  • MAX_DATA: フロー制御で使用する。ピアがコネクション全体で送れるデータ量を増加する
  • MAX_STREAM_DATA: フロー制御で使用する。ピアが各ストリームで送れるデータ量を増加する
  • MAX_STREAM_ID: ピアの使用できるストリームIDの上限を増やす
  • PING: コネクションの生存性を確認する
  • BLOCKED: コネクションレベルのフロー制御によってデータが送信できないことをピアに通知する
  • STREAM_BLOCKED: ストリームレベルのフロー制御によってデータが送信できないことをピアに通知する
  • STREAM_ID_BLOCKED: ストリームID上限のためストリームが開始できないことをピアに通知する
  • NEW_CONNECTION_ID: コネクションマイグレーション用のコネクションIDを発行する
  • RETIRE_CONNECTION_ID: 発行されたコネクションIDを破棄する
  • STOP_SENDING: アプリケーションの都合で、ピアのそのストリーム上でのデータ送信をやめさせる
  • ACK: Ack情報を通知する
  • PATH_CHALLENGE: パスの疎通性を確認を行う
  • PATH_RESPONSE: パスの疎通性の確認に対する応答
  • NEW_TOKEN: クライアントが将来のInitaliパケットで使用するトークンを発行する
  • STREAM: ストリーム上のデータを転送する
  • CRYPTO: 暗号ハンドシェイク用のデータを転送する
  • Extension: 拡張用のフレーム。トランスポートパラメータで合意が得られた場合、任意の拡張フレームを使用できる

アンプ攻撃対策

別途記事を書きました
QUICの仕様におけるアンプ攻撃対策

次書きたい話題

HTTP 418ステータスコードが予約される

「418 I’m a tea pot」としてよく知られる 418 ステータスコードについて、IETF HTTPbis WGで議論になっていました。

「418 I’m a tea pot」はジョークRFCである「RFC2324 Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)」で定義されているステータスコードです。

ここで注意するのは、418はHTCPCPで定義されているステータスコードであり、HTTPで定義されているステータスコードではないという点です。

この議論については以前 Qiita で書いたとおりです。
qiita.com

HTTPで418ステータスコードを予約

以前このブログで書いたとおり、HTTPセマンティクスに関して仕様の再改定が行われております
asnokaze.hatenablog.com

その後、3つの仕様に分割され、HTTPのセマンティクス、HTTP/1.1のフォーマット、キャッシュに関する3つの文書に整理されています。

この改訂版HTTP仕様 (HTTPtre) のdraft-03が本日提出され、そのHTTP Semanticsでついに418が Unused として予約されました

418 (Unused)

418はすでにHTTP実装でイースターエッグとして実装されている可能性があるため、418を予約し、今後も仕様としては418は利用しないことになりました。