ChromeのHTTP/2サーバプッシュサポート廃止検討と、103 Early Hintsについて

ChromeがHTTP/2サーバプッシュの廃止を検討し始めている。またPreload + 103 Early Hintsの有効性について実験していく模様

背景

HTTP/2(RFC 7540) にはサーバプッシュという機能があります。これは、クライアントからのHTTPリクエストをまたずにサーバがHTTPレスポンスを先行して送ることが出来る機能です。

たとえば、index.htmlに画像1,2,3が含まれているようなページがあるとします。このindex.htmlへのリクエストを受け付けたサーバは、クライアントが画像1,2,3がを要求してくることを予測しサーバプッシュでこれらのリソースを送りつけることが来でます。(クライアント側から、そのリソースが不要であればサーバプッシュをキャンセルすることもできます)

f:id:ASnoKaze:20201113000937p:plain

そうすることで、ページの表示を効率化出来ると考えられていました。

しかし、様々な議論の中で、クライアントがすでにキャッシュを持っているケース、優先制御上デメリットがあるなどの様子が見えてきました。そういった話もありつつ、HTTP/2サーバプッシュは広くは使われていないのが現状です。

そんななか、Chromeの開発者のメーリングリストblink-devで「Intent to Remove: HTTP/2 and gQUIC server push」という投稿がなされました。このメールでは、HTTP/2の現状を鑑みてサーバプッシュのサポートを廃止すること、103 Early Hintsの効果測定をしていく旨が書かれています。

Intent to Remove: HTTP/2 and gQUIC server push

現状

過去28日間で、HTTP/2コネクションでサーバプッシュを受け取った割合は0.05%ほどになっています。
Firefoxでも同じぐらいの数値であることが共有されています)

また、パフォーマンスとしてはAkamaiがあまりかわらないか、特定環境でわずかに向上するという資料が共有されています(リンク)

変更内容

ChromeはHTTP/2とgQUIC(Google版QUIC)でサーバプッシュをサポートしています。なおHTTP/3も仕様上はサーバプッシュ機能を持ちますが、Chromeはサポート予定が無いとのこと。

HTTP/2では通信中にSETTINGSフレームで、サーバプッシュを無効にできますので、Chromeはそれを送信するように変更されます。

Prelaod と103 Early Hints

サーバから突然リソースを送りつけるサーバプッシュではなく、Preload方式を取る方向が検討されています。Preloadでは、必要なリソースURLだけをクライアントに通知し実際にそのリソースを取得するか(キャッシュを利用するか)はクライアントが判断します。クライアントは取得するばあいは通常通りのHTTPリクエストを送信します。

Preloadの指示の仕方は「Preload」で書かれています。

たとえば、index.htmlの要求にするHTTPレスポンスをする際に、styles.cssもプリロードしといてねという風に伝えるには下記のようなレスポンスヘッダをつけます。。

Link: <https://example.com/other/styles.css>; rel=preload; as=style

このPrelaod指示をどれだけ早くクライアントに通知するかの工夫の一環で、2017年に
RFC 8297で「103 Early Hints」という新しい仕組みが標準化されています。

100番台のステータスコードは、継続処理を示すもので、本当の200や404レスポンスよりも前にレスポンスすることが出来ます (100番台のあとには最終的なレスポンスを返す必要があります。)
f:id:ASnoKaze:20201112235610p:plain

一般的にステータスコードが決定するのはHTTPリクエストを完全に処理し終わったあとです。ですので重たいサーバ側処理が完了しないと、いかなるレスポンスヘッダも送り始めることは出来ません(100番台レスポンスを除く)。

そういったケースで、最初の103レスポンス時にLinkヘッダでPreload指示を送ればより早いタイミングでクライアントにPreloadを指示できるようになります。

この103 Early Hintsの有効性について、ChromeとFastlyが共同で実験していくというのがちょうど昨今の動きになります。
www.fastly.com

NAT Slipstreaming攻撃とブラウザ側の対策

2021/01/29 NAT Slipstreaming v2が公開されたので、追加記事を書きました
https://asnokaze.hatenablog.com/entry/2021/01/29/014759


2020年10月31日に「NAT Slipstreaming」という攻撃手法が発見されてます
samy.pl

これは簡単に言うと

罠サイトを踏ませることで、SIPのApplication Level Gateway機能を持つNATの内側に居るクライアントに対して、外側からそのクライアントの任意のTCP/UDPポートに接続できる。という攻撃のようです。

この攻撃はさまざなテクニックを使用しており大変面白いです。調査過程も含め詳細は上記のサイトに書かれているので、そちらを読むことを強く推奨します。

ざっくり

登場人物

  • victim(攻撃対象): ブラウザで攻撃者のサイトにアクセスすr
  • attacker (攻撃者): 罠サイトを準備し、最終的にNAT内側のvictimの任意ポートにアクセスする
  • Application Level Gateway NAT: NATの機能を持つが、5060ポートで送信されるSIPメッセージを見つけると、そのSIPメッセージ内に含まれるIP/Portもちゃんと外向きに書き換え、マッピングを行い通信を中継する。

f:id:ASnoKaze:20201107225622p:plain
(解説サイトより引用)

  • 1. victimが、直接もしくは広告を通してattackerのサイトを訪れます
  • 2. attackerはWebRTCやタイミング攻撃を通してvictimのinternal IPを取得します(後のSIPメッセージを構築時に使用します)
  • 3. attackerは、Hidden Formを通して大きなデータを送信させ victim のIPスタックの最大MTUサイズを検出します(後のSIPメッセージをパケット境界に合わせるために使用します)
  • 4. attackerは、Hidden Formを通してvictimに下記のようなPOSTをport5060でattackerサイト宛に送信させます。HTTPボディにはSIPのRegisterメッセージが格納されていますが、このSIPメッセージがちょうどパケット境界になるように途中にデータを埋め込み調整します。(このSIPメッセージのContactヘッダが最終的に攻撃者が通信を行いたい先です)
POST / HTTP/1.1
Host: samy.pl:5060
Connection: keep-alive
Content-Length: 191
Cache-Control: max-age=0
Origin: http://samy.pl

(中略)

------WebKitFormBoundaryhcoAd2iSAx3TJA7A
Content-Disposition: form-data; name="textname"

REGISTER sip:samy.pl;transport=TCP SIP/2.0
Contact: <sip:samy@192.168.0.109:1234;transport=TCP>


------WebKitFormBoundaryhcoAd2iSAx3TJA7A--
  • 5. 上記のSIPメッセージを観測したApplication Level Gatewayは、SIPメッセージの内部IPをグローバルIPに変更しつつ、そのIP/Portをマッピングして中継するようになります。(パケット境界にあわせると、SIPのパケットとして処理されてしまう模様)
  • 6. attackerは受け取ったregisterパケットに含まれるIPとポートに接続することで、NATの内側のvictimの任意のポート(AttackerがRegisterメッセージに埋め込んだポート)に接続できます

UDPでNAT内部アクセスしたい場合は、WebRTC TURNをつかってSIPメッセージを偽装する)

ブラウザの対策

これはブラウザ側の脆弱性というわけではないが、ワークアラウンドとして対策されている。

具体的には5060/5061のポートへのリクエストはブラウザによってブロックされる変更が入っている。

同様に、whatwgの仕様上もブロックポートに加えられている
https://github.com/whatwg/fetch/pull/1109

「RFC7525: TLSを安全に使うための推奨事項」の改定

様々な組織や団体がTLSを安全に利用するためのドキュメントを出してたりする。

IETFでも2015年にRFC7525 「Recommendations for Secure Use of TLS and DTLS」として、使用する上での推奨事項をまとめている。

それについての詳細は、urushima先生の記事を見ていただくと良いと思う。
blog.livedoor.jp

上記のRFC7525から5年が経ち、その間にRFC 8446 TLS1.3の発行などもありました。そのため、IETFではこのRFC7525の改定作業が開始されました

RFC7525bis

IETFUTA (Using TLS in Applications) WGでは、RFC7525の改訂版として、次のドキュメントをすでにWG Itemとして扱っている

ここでは、簡単にRFC7525からの変更点について紹介する

  • TLS1.0とTLS1.1の利用を"SHOULD NOT"から"MUST NOT"に変更
  • TLS1.3/DTLS1.3の利用をSHOULD で追加
  • TLS1.2ではSCSVの実装がMUSTに
  • TLS Encrypted Client Helloへの言及を追加

まだ、Draft 00 ということもあって、差分もあっさり目。

QUICのMultipath拡張に関するメモ

2021/10/26 追記
最新仕様は https://datatracker.ietf.org/doc/html/draft-lmbdhk-quic-multipath-00 こちら
asnokaze.hatenablog.com


QUICのMultipath(マルチパス)拡張に関して、2020年10月にQUIC WG中間会議で議論が行われているので、現状について簡単にまとめる。

はじめに

QUICおよびHTTP/3のドラフト仕様がLast Call (URL)になり、標準化としては大詰めを迎えている。

それにともない、IETF QUIC WGのメーリングリストではQUICのMultipath拡張について、どう取り組んでいくかの議論が盛り上がっている。

Multipath拡張とは、例えばスマホWi-FiLTEのように複数のネットワーク(path)を同時に使ってコネクションを張る方式のことです。Multipathを利用する通信としては、TCPのMultipath拡張であるMultipath-TCPがすでにRFC(RFC 8684)となっており、iOSLinus Kernelでサポートされている。

このMultipathをQUICでも行いたいという話である。

IETFでQUIC WGを設立した際にcharter(URL)として、作業領域および目的を定めたたが、その中にもMultipath拡張を検討する旨記述されている。今標準化を行っている QUIC v1ではこのMultipath機能は取り込まれず拡張仕様として別途議論することと成ったが、そのv1の標準化の終りが見えてきた今、改てMultipath拡張の標準化をどう進めていくかの議論が始まったのである。

まだ、どうなっていくか分からないところだが、中間会議での議論を踏まえて一旦状況を纏めておきたいと思う。

提案仕様

現在、Multipath拡張を実現する提案仕様は、別々に3つ出されている (そのうち2つが同じ仕様名なので注意)。まず最初に簡単に紹介したい。

Quentin De ConinckらによるMP-QUIC

1つ目の「Multipath Extensions for QUIC (MP-QUIC)」はルーヴァン・カトリック大学、Quentin De Coninckらによる提案である。

2017年頃から提案している、Multipath拡張仕様のうち一番古くからとりくまれている仕様。初期に、MPTCP v0のデザインの影響を受けている。Connection ID毎に単方向のUniflowsを持つ、

すでにGoを用いた実装が行われており、論文なども出されている。

QUIC Multipath Negotiation Option

2つ目の「QUIC Multipath Negotiation Option」はPrivate Octopus IncのChristian Huitema氏による提案である。現在のMultipath 拡張の盛り上がりをうけて、今月提出された仕様である。

これは、その他のMultipath拡張とは異なりPathごとの通信識別子を持たず、QUIC v1がすでに持っているコネクションマイグレーションを利用します。マイグレーションとは異なり、複数のPathをactiveなままに維持します。

AlibabaのMultipath Extension for QUIC

3つ目の「Multipath Extension for QUIC」はアリババ勢による提案である。こちらも、現在のMultipath 拡張の盛り上がりをうけて、今月提出された仕様である。

これは、Quentin De ConinckらによるMP-QUICを参考にしつつも、PathごとにSubConnectionとする構成を持つ。どうやら、すでに中国の特定動画ストリーミングサービスでユーザが使えるようになっているらしく、規模を拡大しつつ実験中らしい。

QUIC WG 中間会議

冒頭に述べたように、Multipath拡張をどう進めていくか盛り上がる中で会議を行い、それぞれのユースケースや知見について共有を行う場が用意された。

発表資料および、議事録は 下記から見ることが出来る。
github.com

この会議の中で

  • Googleらによる、gQUIC時代におけるMultipath拡張の実装及び考察が共有された。実装やPathをまたぐ再送制御やスケジューリングが複雑になったこと、多くの場合はコネクションマイグレーションで十分ではないかという話が共有された
  • Apple勢による発表では、MPTCPの利用経験から、SiriやApple Musicの例をあげてQUICのMultipath拡張に求められる要件が共有された
  • アリババグループによる、動画ストリーミングのMultipath事例や、その他のユースケースに説明された。また、動画読み込みが早くなったというデモ動画も共有されている
  • Akamaiユースケースとして、多段キャッシュサーバ構成において2段目からダイレクトにクライアントにレスポンスを返すユースケースが紹介された

f:id:ASnoKaze:20201025233601j:plain

その他にも、衛星通信と地上での通信をMultipathで利用すると言ったユースケースなども共有されている。

これらの議論を通して、Multipath拡張のユースケースと有用性が確認された。

一方で、QUIC WGとしてどのようとりくんでいくかは、進め方の議論をしている段階である。例えば、Martin Thomson氏がメーリングリストに投稿した「My BoF report: multipath」では、Multipath拡張といった大きな取り組みの前に、QUICをデプロイし知見をためつつ、バージョンネゴシエーションといった大きな取り組みの前に準備する必要があると言っています。

その他にも、いくつか目的やデプロイ形式が異なるユースケースを、一つの仕様でカバーしようとすべきではないなどの意見が出ています。

議論の続き

QUIC WGとしてまだ、Multipath拡張をどう進めていくかは議論の最中です。

議論はメーリングリストで行われているので、気になる方はそちらを御覧ください
https://mailarchive.ietf.org/arch/browse/quic/

Permissions PolicyとDocument Policyについて

Chromeに、Permissions PolicyとDocument Policyという仕様の実装が進められています。

今回は、ブラウザがwebページを表示する際に制限を与えるこれらの機能について説明していきます。

仕様は下記から参照できます

あわせて、blink-devメーリングリストのintentやexplainerを読むと分かりやすいかと思います

これらはiframe先へのdelegationについて違いがあります (追記予定)

Permissions Policy

Permissions-PolicyはHTTPレスポンスヘッダで、ブラウザのカメラや位置情報の取得といったpowerful featuresへのアクセス制御を行えます。これによってFeature Policyは置き換えられます。

下記のヘッダ例は、位置情報の取得はself(今表示しているサイト)及びiframeではhttps://foo.com で利用できます。カメラは利用できません。フルスクリーンの機能は利用できます。(なお、このヘッダはStructured Field Values for HTTPに則ってます)

Permissions-Policy:
    geolocation=(self "https://foo.com"),
    camera=(),
    fullscreen=*

上記のヘッダを踏まえて、iframeで埋め込んだページで、各機能へのアクセスを許可するためにallow属性を付ける必要があります。

<iframe src="https://foo.com/" allow="geolocation; camera">
</iframe>

Document Policy

Document PolicyはHTTPレスポンスヘッダで、Webの表示が遅くなる要素を制限することが出来ます。

下記の例では、マークアップ上でサイズが指定されていない画像や、document.writeを無効にしています。また、画像の最大bppを制限し、デフォルトでframeの読み込みをLazy Loadにしています。(このヘッダもStructured Field Values for HTTPに則ってます。)

Document-Policy: unsized-media=?0, document-write=?0,
                 max-image-bpp=2.0, frame-loading=lazy

Permissions Policyと同様にiframe先のページにも制限を課すことが出来ます。

<iframe src="https://img.example.com/"
        policy="unsized-media=?0, max-image-bpp=2.0">

Document Policyでは埋め込んだページを取得する際に、課せられている制限を通知します。
f:id:ASnoKaze:20201004195751p:plain

  • 1. クライアントがexample.comにアクセスし、img.exapmle.com へのiframe要素を処理します。このときiframeのpolicy属性でDocument Policyが指定されています。
  • 2. クライアントはimg.example.comにアクセスします。このときDocument Policyが課せられていることを示すSec-Required-Document-Policyヘッダを付けてリクエストします。
  • 3. img.example.comはDocument Policyの制約を満たすようなレスポンスを返します(Document-Policyヘッダをレスポンスに含める)
  • 4. クライアントは受け取ったDocument-Policyヘッダが、example.comによって課されている制約を満たすことを確認します。

Reporting

Permissions PolicyもDocument PolicyもReporting-APIの機能を持っており、policyに違反があった場合レポートをageさせることが出来ます。

Document-Policy-Report-Onlyヘッダを用いることで、policy違反をブロックすること無くレポートのみを送らせることもできます。

詳しくは仕様を参照ください。

ChromeのSecure context restriction for external requests

[目次]

非セキュアコンテキストなWebサイトからプライベートアドレスへのHTTPリクエストをブロックする「Secure context restriction for external requests」の導入が進められています。

概要

インターネットに公開されているWebサイトから、プライベートアドレスに対するCSRF攻撃が問題になっています。ネットワーク機器やプリンタの管理画面で使われるプライベートアドレスにリクエストを行わせることで、攻撃が行われます。

例えば下記のリンクを埋め込むことで、プライベートネットワークを指すrouter.local にHTTPリクエストを行わせます。

<iframe href="https://admin:admin@router.local/set_dns?server1=123.123.123.123">
</iframe>

具体的には下記のドキュメントを御覧ください

そういった攻撃を防ぐ仕組みが現在検討されています。

  • CORS-RFC1918
  • Secure context restriction for external requests

現在、最初のステップとして後者の「Secure context restriction for external requests」が Chromeで導入が進められているので簡単に見ていく。

Secure context restriction for external requests

CORS-RFC1918に先んじて、「Secure context restriction for external requests」がChrome 87のdev/canary/beta版で導入が予定されています。

これは、非セキュアコンテキストのサイト(http://なサイト)から、プライベートアドレス, ループバックアドレスに対するリクエストをブロックする機能です。

Chrome Canaryではabout:flagsから「Block insecure private network requests.」を有効にすることで動作を確認できます。

実際に、http://example.comで、デベロッパーツールからプライベートアドレスに対してリクエストを送信させています。
f:id:ASnoKaze:20200927190522p:plain

このように ERR_INSECURE_PRIVATE_NETWORK_REQUEST になってリクエストがブロックされている事が確認できます。

CORS-RFC1918

なおCORS-RFC1918 については、以前書いたとおりです
asnokaze.hatenablog.com

W3Cのミーティングでは、Chromeで実装が進められている事が書かれています
https://github.com/w3c/webappsec/blob/master/meetings/2020/2020-09-15-minutes.md#cors-rfc1918

動作確認できるようになったらまた記事を書ければと思います。

Linux 5.6 から Multipath TCPが使える

Linux 5.6から Multipath TCP(mptcp)が使えるようになった。複数インターフェースを使ってTCPコネクションをはり効率のよく通信を行う仕組みです。mptcp v0がRFC 6824で、mptcp v1がRFC 8684で標準化されています。

すでに、iOSでは利用が始まっています。
asnokaze.hatenablog.com

来月リリース予定である、Ubuntu 20.10 は Kernel 5.8 が入っており、mptcpが使えるのか試してみようと思いました。

有効になってることを確認

イメージを公式サイトから落としてきて起動します。

$ uname -a
Linux y 5.8.0-19-generic #20-Ubuntu SMP Fri Sep 11 09:08:26 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

$ sysctl -a |grep mptcp
net.ipv4.tcp_available_ulp = espintcp mptcp
net.mptcp.enabled = 1

すでにmptcpが有効になっていることを確認できます。

試す

redhatさんの記事(URL)では、stapを用いて既存のアプリケーションを変更無くmptcp対応を行っていましたが、うまく行かなかったので自力でmptcp通信を行います。

とは言ってそんなに難しいことはなく、ソケットオプションにIPPROTO_MPTCPを指定するだけで

fd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);

下記ページのコード参考にすれば、比較的容易にクライアントとサーバを用意できました。
www.toumasu-program.net

パケットキャプチャ

通信をキャプチャすると、mptcp v1 で通信が行えていることが確認できます。

f:id:ASnoKaze:20200925004754p:plain

残課題

ちゃんと複数インターフェースでザブフローしゃべるところまで確認したい