QUIC Invariantsの議論

QUICは現在IETFで標準化が進めれています。

QUIC WG出来、本格的に標準化が開始して1年ほど立ったところで、スケジュールとスコープの議論(QUIC - Our schedule and scope)が出てきており、QUIC Version1としてどの機能を標準化する話が行われてる一方で、Invariantsというトピックが出てきています。

QUIC Version 1と今後のバージョンで、パケットのレイアウト・パラメータを不変な値、将来のバージョンで変更されないものとするかという議論です。

TLS1.3の議論で起こっているように、プロトコルのバージョンアップによってファイアウォールなどの中間装置が誤作動し通信が阻害される事があるというのはおおきな課題になってきています。

実際、Google QUICでもパケットのレイアウトを変更した所一部のファイアウォールでおかしな挙動をしたことが、Googleの論文でも触れられています。
asnokaze.hatenablog.com

そこで、QUICでは将来のバージョンをデプロイしやすくするために、変更されないパラメータというのを事前に決めていく流れに有ります。

Version-Independent Properties of QUIC

上記のとおり、QUICのパラメータと意味と場所を固定にする提案は、「Version-Independent Properties of QUIC」として提出されています。

まだ議論の最中ですが、今のところ、各パケットのコネクションID・バージョン番号の位置及び、バージョンネゴシエーションパケットは将来も変更されないとしています。

これによってそのコネクションのバージョンが何なのか経路上からも確認できるので、何かしらの処理があるにせよ非対応のバージョンでおかしな挙動をするファイアウォールは少なくなるものと思われます

ロングヘッダ

ロングヘッダでは、ロングヘッダを示す先頭1bit、Connection ID、Versionの位置が将来的にも固定されます
f:id:ASnoKaze:20171202150839p:plain

ショートヘッダ

ショートヘッダでは、ショートヘッダを示す先頭1bit、Connection IDの位置が将来的にも固定されます
f:id:ASnoKaze:20171202151114p:plain

バージョンネゴシエーションパケット

バージョンネゴシエーションパケットでは、ロングヘッダの固定に加え、バージョンが0であり、続いてサポートバージョンが続く事が将来的にも固定されます
f:id:ASnoKaze:20171202151149p:plain

その他

その他にもこのドキュメントでは、現在のQUICではそうなっているが将来変更される可能性もあるので、幾つかの仮定をすべきでない事も列挙しています。

例えば、ロングヘッダがコネクション確立時のみに使用されるという仮定や、パケット番号の出現位置に関する仮定があげれれています。

QUICのSpin Bitの議論

QUICはUDP上でTCP+TLS+HTTP/2の機能を持つ新しいプロトコルであり、IETFで絶賛標準化中です。7月にあったIETF99より、Spin Bitというトピックが議論されているので簡単に書く。

以前、下記記事にも書いたとおりQUICではACKなどを含む殆どの制御情報も暗号化されており、経路上でパケットを観測しても得られる情報は多くありません。
asnokaze.hatenablog.com

パケットロス率やRTTの情報(タイムスタンプ)はネットワークの最適化に利用されているようで、TCPのようにQUICでもそれらの情報を経路上から見れるようなパケットにするか?という議論の中で出てきたのがSpin Bitです。

Spint Bitはパケットが往復するたびにトグルするフラグで、このフィールドは暗号化されていないので経路上からも観測できます。このフラグを観測することで概ねのRTTが推測できる仕組みになっています。

Spint Bit

Spin Bitの仕様については、コアドキュメントではなく「The Addition of a Spin Bit to the QUIC Transport Protocol」という提案にて定義されている。

f:id:ASnoKaze:20171201003908p:plain

ハンドシェイクで使用されるロングヘッダのRTTを測定するのは容易であるので、Spin Bitのフラグはショートヘッダにのみ存在する。Sで表記されている部分がSpin Bitであり、現在仕様とくらべてフラグ部分の位置がずれただけである。

このSpin Bitは以下のように設定されます

  • サーバが送るSpin Bitは、クライアントから受けっ取ったパケットの内、パケット番号が最も大きいメッセージのSping Bitと同じ値になります。
  • クライアントが送るSpin Bitは、サーバから受け取ったパケットの内、パケット番号が最も大きいメッセージのSpin Bitをトグルした値になります。ただし最初に送るときは0。

全てのパケットが順番通りに送られたとすると、Spin Bitは以下のようにトグルされます。パケットがちょうど1往復したときにトグルされることがわかります。

f:id:ASnoKaze:20171201010247p:plain:w300

しかしタイムスタンプなどを含まないため、サーバ・クライアント側での処理時間や何かしらの待ち時間があるとその影響をうける点や、パケットの順番の入れ替わりに対してはヒューリスティックな対応が必要なようです。

その他

このSpin BitをQUICの仕様に組み込むにあたって、デザインチームによる実験が行われています。
これは実際にQUICの実装にSpin Bitを組み込み幾つかの通信状況でRTTの測定を行うことと、プライバシー上の問題の考察です。

詳しい報告は以下のとおりです。
Latency Spinbit Implementation Experience

RTTの測定について一定の有効性があることが確認されるとともに、プライバシーの問題(特に地理情報の特定等)についても現状(あくまで現状)問題がないという話になっています。

今後もこのようにトランスポートレイヤの変更が議論になる可能性はありますが、このようにデザインとして十分に実験・検討する作業が行われるものと思われます。

チェアである、mnotもMLで触れています
Spin bit discussion - where we're at

TLS over HTTPの提案仕様

追記
関連してATLSの記事を書きまし

Application-Layer TLS の標準化動向
http://asnokaze.hatenablog.com/entry/2018/02/01/084251


TLS over HTTPである。HTTP over TLSではない。

Application-Layer TLS」という提案仕様がCiscoの方より提出されている。(仕様中ではATLSと記述される)

HTTP上でTLSレコードを送受信する仕様である。

TLSレイヤの実装変更はなくレコードをHTTPのボディで送受信できるようにする。なお、httpとhttps両方で使用可能である。

また、TLS1.2とTLS1.3などすべてのTLSバージョンに対応する。

来週からはじまるIETF100でも議論されるようなので、かいつまんで読んで見る。

クライアント

クライアントからの基本的なメッセージは、POSTでJSONを送信する。

Content-Typeにはapplication/atls+jsonを指定する

   POST /atls
   Content-Type: application/atls+json

   {
     "session": "<session-string>",
     "records": "<base64 encoded TLS records>"
   }

サーバ

サーバも基本的には同様である。

   200 OK
   Content-Type: application/atls+json

   {
     "session": "<session-string>",
     "records": "<base64 encoded TLS records>"
   }

正しくないリクエストを受け取った際は400 Bad Requestを返す

ハンドシェイク

各アプリケーションは、内部的に持つTLS実装を用いてセッションを開始する。作成されたレコードはJSON形式に変換されHTTP上で送信される。サーバ側は受け取ったJSONよりTLSレコードを復元しTLS実装に渡し、同様にTLSレコードを送り返す。

f:id:ASnoKaze:20171111223136p:plain

WebSocketの利用

HTTPを利用するとサーバからメッセージを送ることは出来ません。TLS closeメッセージなどサーバから送れるように、WebSocketsに通信をアップグレードすることもできます。

なぜこんなことをするのか

最後になったが、何故このようなことをしているのか、ドラフト中にも書かれている。

f:id:ASnoKaze:20171111230217p:plain
ネットワーク管理者がネットワーク接続をするクライアントに信頼できるルート証明書をインストールさせ、ローカルネットワークの出口などでクライアントとサーバの中間に入ってTLSをほどき、平文を確認し再度TLSセッションを貼り直すような環境を想定している。

そのような環境下でもTLS終端装置(中間装置)を安全に通過できるようにHTTP上でTLSレコードのやりとりをするという話のようだ。

HTTPヘッダに構造定義を与える Structured Headers の提案仕様

HTTPヘッダには、リストや辞書といった構造を表現するのに決まったやり方はありません。

HTTPヘッダ毎にシンタックスや構造が定義されており、そのためパーサーが再利用出来ません。

mnot氏とphk氏の共著で提出された「Structured Headers for HTTP」仕様では、HTTPヘッダに下記の構造を定義しパーサを再利用できるようします。また、新しいHTTPヘッダを標準化する際も個々別のシンタックス定義を不要にしています。

  • Numbers
  • Strings
  • Labels
  • Parameterised Labels
  • Binary Content
  • Items
  • Dictionaries
  • Lists

この仕様はすでに定義されているヘッダを再定義するものではなく、この定義を使用して定義されるヘッダにて使用される想定です

Structured Headers for HTTP

JSON形式を用いるものや、新しい構造を定義して、HTTPヘッダに構造を与える提案仕様は過去にも有りました(IETF97議事録参照)。しかし、どちらも標準化には至っていません。

上記の議論を踏まえて出てきたのが、「Structured Headers for HTTP」という仕様です。

以前の2つに比べ、既存のHTTPの形式に近しい印象です。今月実施される、IETF100(アジェンダ)でも議論される予定です。


以下では定義されている構造について羅列していきます。仕様ではシンタックスとパース方法も書かれていますが割愛

Numbers

整数。IEEE 754倍精度少数。

ExampleNumberHeader: 4.5

パース及び、精度については議論有り。

Strings

ASCII文字列

ExampleStringHeader: "hello world"
Labels

ラベル(小文字のみ)

ExampleLabelHeader: foo/bar
Parameterised Labels

パラメータ付きラベル(順番に意味はない)

ExampleParamHeader: abc; a=1; b=2; c
Binary Content

バイナリデータ(Base64エンコード)

ExampleBinaryHeader: *cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg
Items

number、string、label、binaryのいずれか

Dictionaries

辞書データ(LabelとItemの組からなる)

ExampleDictHeader: foo=1.23, da="Applepie", en=*w4ZibGV0w6ZydGUK
Lists

Itemのリスト(配列)

ExampleLabelListHeader: foo, bar, baz_45

iframe内の転送量を制限する Transfer Size Policy とは

Webページにソーシャルボタンや広告といったiframeを埋め込むことがあります。

この時、埋め込んだ側はiframeの中で使用されるリソースを制限する方法はありません。例えば動画を再生すればトラフィックとCPUが消費されますし、最近では実はBitcoinの採掘に利用されるということもあるかもしれません。

そのようなリソースの消費は、特にモバイル環境においてWebページのユーザ体験を損ねることになります。

そこで、W3CのWICG及びWebPerf WGでは、Transfer Size Policyという仕様が議論されています。まだまだ議論が始まるところで、たたき台というところかと思います。

この仕様は、TPAC 2017のWebPerf WGのミーティングでも議論されるようで、資料がすでに公開されており、アジェンダより確認できます。

docs.google.com

Transfer Size Policy

Transfer Size Policy の仕様はWICGのリポジトリから確認できます
github.com

この仕様では、iframeの属性に転送上限と、上限に達した際に発火するイベントを登録できます。

下記の例では、300KB以上転送した際に、ontransferexceededイベントが発火しonBigFrameが呼ばれます。

  <script>
    function onBigFrame(event) {
      console.log("Frame id: " + event.target.id + " exceeded its threshold bytes in category: " + event.category);
    }
  </script>

  <iframe id="foo" ontransferexceeded="onBigFrame(event)" transfersize="300" src="...">


また、例えば、自ドメインとの通信は無制限、example.comとはスタイルシートは50KBで合計500KBまでといった指定もできるようになっています。

  transfersizes: {"default":"100", "self":"*", "*.example.com":"style:50, total:500"}

この仕組によって、リソースを消費するiframeを検知できるようになる。また、最悪iframeのdomを消すことで対応することも出来るが、別途提案されているPausing Documentsを使うことで処理を一時停止することもできる。

Pausing Documents

あわせて、著者の個人リポジトリですがPausing Documentsという仕組みもあわせて議論されています。
github.com

この仕様では、フレーム内の読み込み・レンダリングスクリプトの実行を停止できるようにする仕組みです。

await frameElement.pause({loading: true, rendering: false, script: false});
await frameElement.unpause();

Transfer Size Policyの仕組みとあわせて、リソースを大量に消費するframeを一時的に停止することができるようです。

Content Security Policy(CSP) レポートのCORSと、Fetchの仕様変更

WHATWG, Fetch詳しくないので間違いがあればご指摘下さい


Content Security Policy(CSP)やHPKPやExpect-CTでは、ユーザエージェントはセキュリティの違反を検出した際に、指定したURLにレポートを送信できる。

例えば、下記のようにHTTPレスポンスヘッダにreport-uriでレポート先を指定します (詳細は それぞれの仕様及び、Reporting APIの仕様を参照)。

Content-Security-Policy: default-src https:; report-uri /csp-violation-report-endpoint/
expect-ct: enforce;max-age=3600;report-uri=https://example.com/report-uri


もちろん、ブラウザが今開いてるURLとは別ドメインのURLを指定することも出来る。これは、特に証明書に関わるセキュリティ違反が起こったときにレポートを送信するので、そのドメインは信頼できないということもある。

expect-ctに関しては、以前書いたとおり
asnokaze.hatenablog.com

report-uriとCORS

今年 7月のIETF99において、expect-ctの標準化に伴いレポート時のCORSに関するISSUEがとりあえげられた(発表資料はコチラ)。

レポート機能におけるCORSをどうするか、preflightを送るのか?送るのであればoriginヘッダに何を設定するかという話である。

選択肢として

  • a. preflight を送る(Originヘッダはnull)
  • b. ホワイトリストに含まれる text/plain で送る
  • c. CORSを違反する

その後の議論によって、expect-ctの仕様ではなくFetch側での議論に場所を移す。

(余談だが、W3C側のCORSの仕様はFetchの方がデファクトになっているため、廃止が検討されている。Proposed Obsolete for CORS)

Document CORS safelist exceptions

Github場でのIssueもFetch側に移行し

その後、一部のContent-Typeを例外扱いにするプルリクエストが出されている(現時点ではマージされていない)
github.com

そのなかでは、「application/csp-report」「application/expect-ct-report+json」「application/report」「application/ocsp-request」を例外としてしている。

これによってレポートを送る際にCORSは不必要にな方向に向かいそうだ。

今月行われるW3C TPACK 2017のWebAppSec WGのアジェンダにもCORSが上げられているのでそこでも議論されるものと思われる。

QUICとネットワーク管理/オペレーションの話

あわせて下記記事も参照のこと
asnokaze.hatenablog.com


QUICは新しいトランスポートであり、IETFで標準化が進められています。

UDPベースのプロトコルであり、QUIC Connection IDと呼ばれるIDでコネクションを管理しており、IPアドレス・ポート番号が変わってもコネクションを維持することが出来ます。また、ほとんどのデータが暗号化されているため、ACKや切断要求なども経路上からは観測できません。

このように様々な機能改善が行われており、ファイアウォールといった経路上で機能をもつ装置ではQUICを適切に扱う必要があります。

IETFのQUIC WGでは、QUICトラフィックを管理するネットワークオペレータやミドルウェアの管理者向けに「Manageability of the QUIC Transport Protocol」を出しています。

この文書では、経路上でどのような情報が見え、どのような情報は見えないのか、既存のプラクティスがどのような影響をうけるかについて記述しています。

前半はQUICのワイヤイメージと機能に関して、後半はファイアウォールやDDoS対策などネットワークオペレーションへの注意事項が記述されています。ただしすべき/すべきでないといったプラクティスは環境に依存するため、この文書では書かれていない

かいつまんで紹介する

ワイヤイメージ

QUICのトランスポートを定義する「QUIC: A UDP-Based Multiplexed and Secure Transport」では、ロングヘッダとショートヘッダを定義しています。ロングヘッダはバージョンネゴシエーション、サーバリトライ、0-RTTデータを含むコネクションの最初に使用されます。一方、ショートヘッダはハンドシェイク後に使用され、ヘッダのオーバヘッドを抑えるために殆どのデータパケットで使用されます。

ロングヘッダ

    0                   1                   2                   3
    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|   Type (7)  |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +                       Connection ID (64)                      +
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Packet Number (32)                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         Version (32)                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                          Payload (*)                        ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

ショートヘッダ

    0                   1                   2                   3
    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|C|K| Type (5)|
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                                               |
   +                     [Connection ID (64)]                      +
   |                                                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                      Packet Number (8/16/32)                ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                     Protected Payload (*)                   ...
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • Type: ロングヘッダはヘッダタイプフィールドを持ちます。現在のQUICバージョンは6つのヘッダタイプを定義しており、Version Negotiation, Client Initial, Server Stateless Retry, Server Cleartext, Client Cleartext, 0-RTT Protected です(将来増える可能性があります)。
  • Connection ID: コネクションの識別子。QUICでは、5タプル(送信先/送信元IPアドレス送信先/送信元ポート、IPヘッダのプロトコル)ではなくConnection IDでコネクションを管理します。サーバから採番もできます。ショートヘッダではオプショナルのフィールドです。
  • Packet Number: 各パケットはバケット番号を持ち、送信する毎に増加していきます。乱数で初期化された内部カウンターのうち最下位ビットの8, 16, 32bitのいづれかを長さで格納します。再送の場合でも値は増加すること、値はスキップ出来ることに注意が必要です。
  • Version Number: QUICのバージョンです。バージョンはVersion Negotiationパケットでサポートしているバージョンを送りますが、暗号化されていないため送信者側のサポートしているバージョンは経路上からも確認できます。
  • K(key phase):ショートヘッダはKey Phaseフラグを持ち、暗号化に用いた鍵を識別するのに使用されます。


これらのフィールドの位置は将来のQUICでも固定されているでしょうが、フィールドは増える可能性がある点に注意が必要です。

また、暗号化されていないフィールドにおいてもコネクションが確立した後に値は検証されるため、経路上では1bitも変更はできません。変更した場合は、パケットが破棄されるか、コネクションは切断されます。

バージョンネゴシエーションにおいては、TLSのGREASE拡張のように未知のバージョンを含められる点に注意が必要です。

経路上で出来ること

QUICトラフィックの識別

QUICトラフィックは特別、その他のUDPパケットと区別出来るようには設計されていません。

現在ターゲットなっているアプリケーションプロトコルはHTTPであり、HTTP over QUICは通常UDPポートの443を使用しますが、他のポートも使用できます。

バージョンの識別

上記のように確実ではありませんがパケットがQUICだとして観測するならば、ハンドシェイクを観測することでバージョンを推定できます。バージョン番号を持つClient Initialパケットと、サーバからそのバージョン番号を持つ平文のパケットが送り返されていれば、バージョン番号が受理された事を意味します。

ただしクライアントやサーバのIPアドレスが変わってもコネクションは維持されるため、その際ハンドシェイクは行われません。Connection IDをトラックし、最初のハンドシェイクの情報が必要になります。

不正なパケットの拒否

ネットワークフィルタで、反射攻撃やバックスキャッタといった不正なパケットを拒否したい場合があります。

パケットの最初のバイト(パケットタイプ)に基づくヒューリスティックな手法で、そのパケットタイプが届くことが正しいのか分類することが出来ます。

ただし、パケットタイプは将来のバージョンでは増えたり、異なる意味を有する可能性があります。

コネクション確立の確認

コネクションを確立するには平文のパケットが必要であり、ストリーム0上でTLSハンドシェイクを行います。そのため、TLS over TCPど同じような手法を用いて検出することが出来ます。

通信フローの関連付け

例えば、NATリバインディングIPアドレスの移動などで、クライアントのIP・ポートが変わってもConnection IDは変わらないので前後の通信フローを関連付けできます。

通信フローの切断検出(不可)

QUICはコネクションの終了は経路上からは分かりません。

通信フローが終了した場合、経路上の装置からはパケットが観測されないだけです。NATやファイアウォールのような状態を持つ経路上の装置は内部に持つQUICのフロー状態を消すアイドル時間を設定する必要があります。

ラウンドトリップタイムの計測

QUICではACKも暗号化され経路上では確認できないため、ハンドシェイク中にのみ通信を観測し測定できます。

この振る舞いに関する変更は議論中です。https://github.com/quicwg/base-drafts/issues/631 参照。

パケットロスの計測

QUICパケットは平文でパケット番号を送信します。パケット番号のギャップを観測することで観測点より上流でのロスを推定できますが、通常推奨はされませんがパケット番号を飛ばすことも許可されています。

また、ACKや再送は経路上で観察できないため、観測点より下流でのロスについては正確に推定できません。

ネットワーク経路上の装置への影響

QUICトラフィックの状態を扱う装置

ファイアウォールなどの、QUICトラフィックの状態扱うのはコネクションの確立を観測することで可能です。

ただし、終了は経路上からはわからないので最後の通信からのタイマーや、LRU(Least Recently Used)方式で状態は削除されるべきです。ただし、アプリケーションの要件に依存します。

ネットワークのパフォーマンス測定とトラブルシューティング

QUICトラフィックを受動的に観測することにより、極めて限定された損失とRTT測定が可能です。

サーバと協調したロードバランシング

Connection IDはサーバから払い出すことが出来ます。Connection ID(64bit)は任意のバイト列ですので、特定の情報を埋め込む事もできます。複数ロードバランサがある場合などでも、最初に処理した(通信を復号できる)ロードバランサの識別子などを埋め込んでおくことで、DNSラウンドロビンなどで別のサーバにパケットが届いても、適切なロードバランサにルーティングすることもできます。

ただしクライアントが悪意を持って変更しても大丈夫なように、HMACなどを用いてで不正なパケットを検出できるようにすべきです。

DDoSの検出と緩和

現在のプラクティス通り、正常なトラフィックと異常なトラフィックを分類することになります。

QUICのステートを見ることで不正なパケットを分類できますが、正当なQUICクライアントを用いたDDoS攻撃には意味がありません。

Connection IDを用いたコネクションのマイグレーションを使用すると、5タプルだけでなく多くの情報を保持する必要がありますが、DDoS攻撃の中でコネクションのマイグレーションを受け付ける必要があるかは疑問があるようです。

QoSとECMPのサポート

この文書のQoSに関する記述はまだ推測であると注意書されています。

QUICでは5タプルが同じであれば経路上で同じような処理をうけると仮定しており、多重化せれた全てのストリームのメッセージは同じように輻輳制御をうけます。ネットワークはQoSに5タプルの他にコネクションを用いることも出来ます(MAY)。

QUICはTCPよりもパケットの順番の入れ替わりに耐性があることを考えると、ECMPは必ずしもフローベースである必要はありません。