新しいユーザエージェント表現 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/11/08 追記
IETF103において、QUIC WGとHTTPbis WGそれぞれでHumが行われ
HTTP over QUICの名称をHTTP/3とするコンセンサスが得られました

QUIC WG

HUMS ON SUPPORTING RENAMING to H3
support hum strongly in favor with non zero do not support. perhaps 70:30
support hum for letting httpbis wg decide: ~100% support

HTTPbis WG

Hum: in favor of 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プロトコルの簡単なまとめ)

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は利用しないことになりました。

Auto Upgrade Mixed Contentとは

HTTPSのサイト内にHTTPで提供される画像やスクリプトがあると、「Mixed Content」の仕組みによりURLバーに黄色い警告が出たり、リソースがブロックされます。

もちろんスクリプトがブロックされればWebページを正しく表示できません、URLバーに表示されたシールドマークから通信を許可することもできますが、一般ユーザにとって分かりにくいUXとなっています。

そこでChromeでは、HTTPで読まれるリソースをブロックするのではなく、HTTPSに自動でアップグレードしてリソースを取得する「Autoupgrade Mixed Content」という仕組みが検討されています。

以前、GoogleのMike West氏から提案されていた「Mixed Content Level 2」と似たような仕組みです。
asnokaze.hatenablog.com

( Auto Upgrade Mixed Contentの資料には「THIS DOCUMENT IS PUBLIC」と書かれてるので、おそらく大丈夫)

実験

Chromeでは、6通りの方式を実験してどのようなアップグレード手法が有効か模索しようとしています。この実験を解析し、新しい機能を改めて提案するかが決定されます。

3種類のアップグレード方式と、それぞれについてHTTPSのロードに失敗した場合HTTPにフォールバックするかの2種類を試し、計6種類を実験するようです。

stableのユーザのうち1%で実験を行うと書かれています。この実験では、画像など今までブロックされていなかったリソースもフォールバックなしだと表示されなくなる場合がありそうです。

方式

Upgrade blockable mixed content to HTTPS

Scriptなどのblockable mixed contentをアップグレードします。すでにブロックされているリソースですので、HTTPSにアップグレードしてリソースが読み込めなくてもページを破損させることはありません。

Upgrade only optionally blockable mixed content to HTTPS

画像などのoptionally blockable mixedのみをアップグレードします。通常、表示できているリソースですので、HTTPSににアップグレードすることで読み込めなくなり、ページを破損させる可能性があります。

Upgrade all mixed content to HTTPS

すべてのmixed contentをアップグレードします。このケースでは、警告表示や、ユーザによるmixed contentの許可はなくなります。

サイトがupgrade-insecure-requests ヘッダを指定した場合と似た挙動となります。

フォールバック

HTTPSにアップグレードを試みて、リクエストが失敗したりタイムアウトした場合はHTTPにフォールバックします。このフォールバックは、表示速度が遅くなるとともに処理も複雑になることが懸念されています。

NTPを暗号化する Network Time Security for NTP の提案仕様

サーバが正しい時刻に設定されていることは重要であり、時刻同期に使用されるNTPにおいてもサーバの認証や改ざんを防ぎたいというモチベーションは理解できる。

IETFのNTP WGでは、まさにNTPを暗号的に保護するNetwork Time Security for the Network Time Protocol」という仕様策定が進められている。

NTPにはいくつかモードがあるが client-server モードを前提としている。

NTPは詳しくないがざっと見てみる

(正しくはNTPの暗号化ではなく、保護的な感じはある)

目的

Network Time Securityの目的は以下の通り

  • Identity: x.509 証明書で通信相手を確認する
  • Authentication: 時刻同期のパケットを認証し、改ざんされていないことを確認する
  • Confidentiality: 時刻情報に秘匿性はありませんが、NTP拡張フィールドの暗号化をサポートする
  • Replay prevention: 以前のパケットが再送されても検知できるようにする
  • Request-response consistency: クライアントの要求に対する応答で有ることを確かにする
  • Unlinkability: クライアントが別のネットワークに移動して通信を行ったとしても、経路上からは同一クライアントか識別できないようにする
  • Non-amplification: 増幅攻撃が行われないように、リクエストより大きなレスポンスを返さない
  • Scalability: サーバはユーザ固有の状態を保存せず、多くのクライアントにサービスを提供できるようにする

プロトコル概要

このNTSは2つの疎結合のサブプロトコルからなります

暗号化するための鍵を交換するNTS Key Establishment と、NTPv4を暗号化してやりとりするための拡張を定義したNTS Extensions for NTPv4です。

f:id:ASnoKaze:20181005023438p:plain

NTS Key Establishment (NTS-KE)

NTPv4のNTS拡張領域で使用するキーマテリアル(お馴染み RFC5705)を取得するメカニズムです。

TLSを使用して鍵を交換し、クライアントに最初のCookie、NTPサーバのIP等を提供します。また、暗号アルゴリズムなどいくつかのパラメータをネゴシエーションします。

NTS Extensions for NTPv4

NTPv4でNTS用の拡張領域を持ちます。この領域によって正しいサーバと通信できており、時刻情報が改ざんされていないことを確認できるようになります。

具体的には以前に取得したキーマテリアルを使用してNTPv4を暗号的に保護する複数の拡張領域が定義されます。

すべての状態はクライアント側に保持され、サーバはクライアントの状態を保持する必要がないため、スケーラビリティがあります。これらの拡張フィールドには、NTS-KEハンドシェイクから抽出されたキーマテリアルを使用して計算されたCookieと認証タグが含まれます。

NTPサーバはこのクッキーを使用してこのキーマテリアルをリカバリし、認証されたレスポンスを返します。 レスポンスには新しく暗号化されたCookieが含まれます。クライアントは次はこのCookieを使用します。こうすることで、返されたレスポンスは自身のリクエストがあってから生成されたレスポンスであることが確認できます。

実装

すでにいくつかの実装が出ている

その他

時刻同期プロトコルでサーバ認証を行う際、クライアントの時刻が同期されておらずサーバ証明書の検証が失敗される問題が有る。このドキュメントでも完璧な解決策は無いと書かれている。いくつかの緩和策は述べられていますが、やはり本質的な改善では無いようです。

JS Self-Profiling API とは

W3C Web Performance Workingの議事録に「JS Self-Profiling API」についての議論があったので簡単に眺めておく。

ミーティングの「発表スライドはこちら

JS Self-Profiling API

いわゆるRUM(Real user monitoring)などと同様、実際のユーザ側でJavaScriptのプロファイルを取得可能にするというのが「JS Self-Profiling API」のようだ。

f:id:ASnoKaze:20181004002206p:plain

ユーザにより端末やネットワーク環境が違うため、実際のユーザ側でJavaScriptのプロファイルを取りたい。timerを使うことで擬似的に測定はできるが、コード量やオーバヘッドが増える。この提案を行っている、Facebookの人らはJavaScriptとSharedArrayBuffersでサンプリングプロファイラを実装したが正確性とパフォーマンスの欠点があると述べています

といった背景から、このような提案が出ているようだ。

使い方

プロファイルを取るサンプルコードが出ている。
performance.profileでプロファイリングを開始して、stopして得られた結果を送信するような流れである。

const profiler = performance.profile({ categories: ['js'], sampleInterval: 10 });
for (let i = 0; i < 1000000; i++) {
     await doWork();
}
const trace = await profiler.stop();
sendTrace(trace);

profileデータ

得られたデータのフォーマットは、V8のTrace Event FormatGecko profile formatを参考に作られているようだ。

interface ProfilerTrace {
  readonly attribute FrozenArray<ProfilerFrame> frames;
  readonly attribute FrozenArray<ProfilerStack> stacks;
  readonly attribute FrozenArray<ProfilerSample> samples;

  [Default] object toJSON();
};

framesから実行状態を取れる模様