HTTP/2の改定版仕様の変更点について

2015年に標準化された、RFC7540 HTTP/2 の改定作業が進められています。

作業中のドキュメントは「http2bis」として公開されている。

現時点で含まれている変更点を見ていきます。なお、今後も変更が入る可能性はあります。

HTTPセマンティクス仕様を参照

もともとのHTTP/2の仕様では、HTTPメッセージのセマンティクスについて既存のHTTP/1.1の仕様を参照していました。しかし、HTTP/1.1の仕様はHTTPセマンティクス仕様に分離整理されました。

f:id:ASnoKaze:20211004003333p:plain

それにあわせて、改訂版ではその新しいHTTPセマンティクス仕様を参照しています。ヘッダやボディといった用語について変更された部分があるので、HTTP/2の改訂版仕様でも用語の使い方をあわせています。

セマンティクス側の変更点について以前書いた記事を参照ください。

asnokaze.hatenablog.com

TLS 1.3 への対応

もともとのHTTP/2の仕様は、TLS 1.3が出る前に標準化されました。その後、RFC8740「Using TLS 1.3 with HTTP/2」としてHTTP/2においてTLS1.3を使う場合の仕様が記述されています。

今回の改訂版は、RFC8740を取り込みつつ、TLS 1.3の使い方について言及しています

  • RFC8740に則り、post-handshake authenticationの禁止される
  • NewSessionTicketおよびKeyUpdateはHTTP/2に影響がないので、そのまま使用できる
  • RFC8470 に準じていれば、TLS 1.3 の 0-RTT Early DataでHTTPリクエストを送信出来る

RFC7540で定義される優先度制御の廃止

もともとHTTP/2で定義された依存関係を指定する優先度制御方式は、複雑すぎるということで廃止されました。以前紹介した、新しいHTTP優先度制御方式の使用が推奨されます。
asnokaze.hatenablog.com

改訂版では、具体的な優先度制御に関する記述が削除されました。ただし、既存のHTTP/2実装と互換性を維持するために、PRIORITYフレームやHEADERSフレームのフォーマットは変更はありません。既存のPRIORITYフレームを受信した場合は単純に破棄されます。

アップグレードを廃止

もともとの仕様では、HTTP/1.1でつないでから、その通信をHTTP/2にアップグレードする手順が定義されていました。ほぼ使われていないため、この方式は廃止されました。

フィールドのバリデーションを厳密に行う

ヘッダなどのフィールドにおいて、どのような文字が許容されるか、検証項目がより厳密になりました。

HTTP/2からHTTP/1.1に通信を中継するリバースプロキシの実装で、いくつかの脆弱性が見つかったため、より厳密に言及されるようになりました。

基本的には実装上の脆弱性ですが、具体的な攻撃手法は、「HTTP/2: The Sequel is Always Worse | PortSwigger Research」などを参照ください。

Experimental Useを開放

Experimental Useとして予約されていた拡張フレームタイプ値・SETTINGSパラメータ値を開放

Hostヘッダと:authority疑似ヘッダが一致すること

Hostヘッダと:authority疑似ヘッダが必ず一致することが必須となりました。

これも、HTTP/2からHTTP/1.1にプロキシする場合に、曖昧な部分があったため、厳密化されました

HTTP Datagram PING の拡張仕様についてのメモ

HTTP Datagram PING」という提案仕様をGoogleのBenjamin Schwartz氏がIETFに提出している。

これは、HTTP/3でのDATAGRAMフレームにおいて、PINGを送れるようにするものである。HTTP/3におけるDATAGRAMフレームの利用については以前紹介したとおりである。
asnokaze.hatenablog.com

目的

WebTransportやMASQUEでは、DATAGRAMフレームはプロキシを超えてEnd-to-Endでのやりとりに利用できるように設計されています。このHTTP Datagram PINGを使う目的は主に次のとおりである

  • End-to-Endにかけてコネクションが維持されており、通信が出来ることを確認する
  • End-to-Endで通信のレイテンシを確認する
  • End-to-EndでPath MTU Discoveryを行う

特に、3つめにあげたのが、この提案の主題でもあります。

もともとMASQUE WGでは、HTTPプロキシを超えたPath MTU Discoveryについてどのように行うか議論がありました。例えば、CONNECT-IPでは、MASQUE Proxy先のMTUを考慮してクライアントはDATAGRAMフレームを送信しなければなりません。その問題に対して一つの提案として出されたのがこの仕様です。


具体的には、トランスポートレイヤでのPath MTU Discoveryを説明した「RFC8899」で定義されている通り、DPLPMTUD Probeパケットの送信に使用できます。

なお、CONNECT-IPは以前紹介したとおりです。
asnokaze.hatenablog.com

(この仕様では、MASQUE Proxyを超えてはPath MTU Discovery出来ない?ちょっと、そこを理解できてない。)

HTTP Datagram PING

具体的なHTTP Datagram PINGの中身を確認します。

最新版の「Using Datagrams with HTTP」では、DATAGRAMフーレムに拡張性を持たせている。HTTP Datagram Format Typeとして、新しいFormat Typeを定義できる。

HTTP Datagram PING」では、新しいFormat TypeとしてPING Datagram Formatを定義している。

QUICのDATAGRAMフレーム及び、REGISTER_DATAGRAM_CONTEXT Capsule Formatも含め記述すると次のとおりである。
f:id:ASnoKaze:20210927010028p:plain

PING Datagram Formatは、シーケンス番号と任意のOpaqueデータを含みます。

PINGフレームを受け取ったエンドポイントは、同様にPINGフレームを応答する必要があります。

大体の位置情報を示すSec-CH-Geohashヘッダ

Private Relayをはじめ、Proxyを通してクライアントのIPアドレスをサーバに対して隠す技術が登場しています。

ところで、Webサービスはクライアントの位置情報によって出力する情報を変えたり、最適化を行う場合があります。JavaScriptのGeolocation APIから取得することもあるでしょうが、APIアクセスなどJavaScriptが利用できない場合もあるでしょう。その場合、IPアドレスから位置情報を類推する手段(GeoIPなど)があります。

IPアドレスから位置情報をある程度推測する手法は、IPアドレスが隠されると機能しなくなります。

Private Relayではユーザが望む場合は、Webサーバに対して大体の位置情報を提供するという観点について、IETF 111の発表で触れています。

f:id:ASnoKaze:20210922010718p:plain
(https://datatracker.ietf.org/meeting/111/materials/slides-111-pearg-private-relay-00)

以下の2つが上げられています

  • egress proxyを選択
  • Geohashを Client Hint で提供する

The Geohash HTTP Client Hint

大体の一を示すGeohash をヘッダでサーバに提出する方法「The Geohash HTTP Client Hint」の仕様が、Appleの方よりIETFに提出されています。(なお、この方法が、実際に使用されているかは定かではありません。)

Geohashは任意の精度で緯度経度を示す方法です。近い文字列は近い場所を示します。また、末尾から文字を削るほど精度が低くなります。
ja.wikipedia.org

HTTP Client Hintの仕組みに則り、Webサーバがサポートしている場合、クライアントが望めばSec-CH-GeohashヘッダでGeohashを提出します。

動作例
  • 1. まずクライアントはWebサーバにHTTPリクエストを送る
  • 2. サーバは、HTTPレスポンスヘッダのAccept-CHでSec-CH-Geohashをサポートしていることを伝える
  Accept-CH: Sec-CH-Geohash
  • 3. クライアントは、次のHTTPリクエストからSec-CH-Geohashでジオハッシュを送信する
Sec-CH-Geohash: "u4pruydqqvj"

u4pruydqqvjは、緯度経度(57.64911,10.40744)を示します。

RSAブラインド署名を用いたHTTP認証の提案仕様

The Privacy Token HTTP Authentication Scheme」という提案仕様が、AppleとCloudflareの方らによって提出されています。

これは、Proxy-Authorizationヘッダで指定できる新しいHTTP認証です。RSAブラインド署名を使います。クライアントを認証するサーバは、特定の鍵で署名されている事は分かるが、クライアントを識別できません。

RSAブラインド署名そのものについては、「RSA Blind Signatures」にて、署名や検証の方法が書かれている。

この仕様では、RSAブラインド署名データを含むPrivacy Tokenを規定する。しかし、クライアントがそれをどのように取得するかは規定されていない。

具体的なユースケースについては、今後出てくるものと思われる。

PrivacyToken 認証方式

このHTTP認証では下記のとおりである。Proxy-Authorizationヘッダで、認証方式としてPrivacyTokenを指定し、base64urlエンコードしたPrivacyTokenの中身が続く。

Proxy-Authorization: PrivacyToken token=abc...
PrivacyToken

PrivacyTokenは次のような構造を持ちます

struct {
    uint8_t version;
    uint8_t key_id[32];
    uint8_t message[32];
    uint8_t signature[Nk];
} Token;
  • version: この仕様では1です。
  • key_id: 署名を生成するのに使用した鍵のハッシュ値
  • message: 32オクテットのランダムな値。署名される
  • signature: messageへのRSAブラインド署名

クライアントはmessageに対してRSAブラインド署名をしてもらいます。署名者が何かしらの方法でクライアントを認証してたとしても、messageの中身を知ることは出来ないため、以後使用されるmessageの値をもってしてクライアントを識別することは出来ません。

(署名者と、PrivacyToken認証を行うサーバは同じ主体とは限りません)PrivacyToken認証を行うサーバは、署名者によって署名されている事は分かります。しかし、クライアントを特定することは出来ません。

QUICのバージョンネゴシエーションとダウングレード攻撃対策

QUIC Version 1がRFC 9000として標準化されました。IETF QUIC WGでは、QUICの次のバージョンを見据えて、バージョンネゴシエーションの仕組みについて検討を始めています。

その仕組は「Compatible Version Negotiation for QUIC」としてWG Draftとなっています。この仕様は、version 1に限定された仕組みではなく、将来標準化されるQUICバージョンにも適応できるバージョンネゴシエーションの仕組みを定義しています。

将来標準化されるQUICバージョンがどのようなハンドシェイクを行うかは具体的には未定である点に注意してください。例えば、QUICv1では、TLS1.3のハンドシェイクをInitialパケットで開始し鍵を共有していましたが、将来のQUICはそうであるとは限りません。

将来に渡ってQUICが持つ不変事項については、「RFC 8999: Version-Independent Properties of QUIC」を参照してください。特に「Appendix A. Incorrect Assumptions」には、具体的な正しくない不変事項が書かれているため、理解の助けとなります。

Compatible Versions

Compatible Version Negotiation for QUIC」では、「互換性のあるバージョン(Compatible Versions)」という概念を定義しています。

QUICの通信は、クライアントからのロングパケットで開始されますが、バージョンAとバージョンBのパケットが、ワイヤ及び動作が同じ場合に互換性があるとしています。互換性のあるバージョンでは、ハンドシェイクをそのまま進める事ができます。ハンドシェイク後の動作は異なるかもしれません。

バージョンBがバージョンAのスーパーセットな場合も考えられます。バージョンAはバージョンBのパケットとして処理できるが、逆は出来ない場合もある点に注意してください。

なお、各バージョンが互換性があるかはそのバージョンの仕様の中で言及されるものです。

互換性のあるバージョン間と、互換性のないバージョン間のハンドシェイクの流れについてそれぞれ「Compatible Version Negotiation for QUIC」の中で定義しています。

Compatible Version Negotiation

バージョンAとバージョンBが互換性のある場合、は一つのコネクションのなかでハンドシェイクが進行します。コネクションを確立するのに追加のラウンドトリップは発生しません。
Version Informationについては後述します。

f:id:ASnoKaze:20210913001607p:plain

Incompatible Version Negotiation

互換性がないバージョンAから、バージョンDに切り替えてハンドシェイクを行う場合です。クライアントからの最初のパケットに対して、RFC 8999で定義されているVersion Negotiationパケットを返します。この時、サーバがサポートしているバージョンの一覧をクライアントに通知します。この流れでは、追加のラウンドトリップが発生します。

クライアントは、サーバがサポートするバージョンのうち自身が対応しているものがあれば、そのバージョンでハンドシェイクを再試行します。このとき、このハンドシェイクは別のコネクションとなります。

f:id:ASnoKaze:20210913004154p:plain

Version Information

クライアント及びサーバ(群)が対応しているQUICバージョンを相手に通知する仕組みです。QUIC version 1ではトランスポートパラメータで送信されます。

Version Informationは次の情報が入ります

  • Chosen Version: 選択したバージョン。
  • Other Versions: サポートしているその他のバージョン。サーバ側から送る場合は、冗長性のために配置されているサーバ全てで使用できるバージョンが記述されます。また、グリスのために0x?a?a?a?aが含まれうる。

クライアントは、Version Negotiationパケットをトリガーしたバージョンが、Version Informationに含まれていた場合などは、ダウングレード攻撃対策としてVERSION_NEGOTIATION_ERRORエラーでコネクションをクローズします。

参考

2019年の記事ですが、参考にさせていただきました
qiita.com

Bootstrapping WebSockets with HTTP/3 の仕様

既存の仕様ではHTTP/3上でWebSocketは使うことができません。

そこで、HTTP/3でWebSocketを使えるようにする「Bootstrapping WebSockets with HTTP/3」という仕様が、GoogleのRyan Hamilton氏によって提案されています。

背景: HTTP/2とWebSocekt

もともと、HTTP/2でWebSocketを使うためにRFC 8441「Bootstrapping WebSockets with HTTP/2」という仕様があります。この仕様では、一つのストームをWebSocket通信として使うために、:protocol 疑似ヘッダやSETTINGSパラメータを導入しています。
asnokaze.hatenablog.com

Bootstrapping WebSockets with HTTP/3

この仕様自体は、RFC 8441「Bootstrapping WebSockets with HTTP/2」の仕組みを同様にHTTP/3に導入するものです。

この仕様は、RFC 8441で導入されたExtended CONNECTメソッドと、SETTINGSパラメータをHTTP/3に導入する単純な仕様になっています。

感想

WebSocket over HTTP/3では、単純にHTTP/3に乗せるだけでも、ストリームを超えたHoLBがなくなったり、コネクションマイグレーションに対応できるといったメリットがあるものと思います。

別の議論として、HTTP/3の利点を活かす、JavaScript APIの変更も含めたWebTransportという仕様が提案されています。このWebSocket over HTTP/3では、WebTransportが提供する単方向ストリームや再送を要求しないデータ通信(DATAGRAM)といった機能は使えません。一方で、既存のWebSocketのAPIを流用するため既存のアプリケーションでもそのまま動作するものと思います。

また、現在 WebTransportやMASQUEの文脈でHTTPセマンティクスにおける、CONNECT拡張について議論が行われている最中です。そこの部分整理があるかもしれません。

(あとは、Bootstrapping WebSockets with HTTP/2から単純にマップ出来ないところがありそうな気はしている。エラーコード周りとか。)

ホスト名の最後が数字なURLの扱いについて

WHATWG URL」の仕様で、ブラウザにおいて、ホスト名がIPv4でないが、数値で終わるURLは拒否されるように変更された。

github.com

例えば次のようなものです

  • foo.0
  • bar.0.09
  • 1.2.3.4.5

今まで、これらのホスト名は、その他のドメインと同じように、通常通り名前解決されます。Issueの起案者は、すでに具体的な攻撃があるわけではないとしつつも、紛らわしさや、eTLD+1 (same-site)の扱いの問題があるとのことです。

なお、次のようなものはIPv4アドレスにマッピングされアクセスできます。

Deprecate support for URLs with non-IPv4 hostnames ending in numbers

実際に、Chromeでは、そのような変更を入れる検討が始まっています。URL仕様の変更であり、http, https, ws, wss, fileなどで対象となるURLが拒否されます。拒否は、Google's URL parsing libraryのGURLで行われます。

Intent to Prototype: Deprecate support for URLs with non-IPv4 hostnames ending in numbers

なお、現状そのようなURLの名前解決が(0.0003%ほど)行われているようですが、多くのものが入力ミスなどと考察されています。hostsファイルを指定している可能性もありますが、詳細なデータはなさそうです。

動作確認

Chrome Canaryに変更が取り込まれたので動作確認する。
今までは名前解決が出来なかったというエラーだったが、URLパースエラーとなっていることがわかる。

f:id:ASnoKaze:20210827125848p:plain

URLバーに入力した場合は、URLとしてではなく、検索キーワードとして処理される模様