Webサイトを離れたときにデータを送る Page Unload Beacon

Webサイトを離れたときにサーバにデータを送れるようにする「Page Unload Beacon」という仕組みが、W3CのWICGで議論されています。

既存のページのライフサイクル(unloadイベントやbeforeunload)で、サーバにデータを送ろうとしても処理されないことがあります。そのため、ページのunload時にビーコンを送るように登録できるようにするのが「Page Unload Beacon」です。

最新のChrome Canaryでとりあえず動くっぽいので、触ってみる (まだ動作するだけで、一部仕様と異なります)

Page Unload Beacon

デベロッパーツールから次の通り実行して、Beaconを登録しておきます。今回はGETリクエストとPOSTリクエストのビーコンをそれぞれ登録。

getbeacon = new PendingGetBeacon("http://example.com/?get");
postbeacon = new PendingPostBeacon("http://example.com/?post");
postbeacon.setData("post data")

その後、Webページを閉じます。

WiresharkでHTTPリクエストを引っ掛けると、次の通りリクエストが飛んでる事ができます

おまけ

  • 通常のBeacon APIと同じようにCookieは付くようです (まだ付かない?)
  • 通常のBeacon APIと同じであれば、「Private Network Access」の制約を受けそう (まだ制約されない?)
  • ネットワークが切断されて切り替わった場合は、ビーコンは送られないぽい (未確認)

ここらへんは実装が進む中で変わりそう

DNS名前解決エラーもネガティブキャッシュする提案

Negative Caching of DNS Resolution Failures」という提案が、Verisignの方らによって提案されています。

DNSの名前解決の結果はつぎのいずれかです。

  • 1) 要求されたデータを含む応答
  • 2) 要求されたデータが存在しないことを示す応答
  • 3) ネットワークエラーや、データ不整合などの、有用な情報が得られない(失敗)

今回の提案では、(3)のエラーについても最低5秒間ネガティブキャッシュするよう要求します(5分以上キャッシュしてはいけない)。

RFC2308 「Negative Caching of DNS Queries」では、サーバが落ちていたり接続できない場合に、オプショナルでキャッシュする事が記述されてはいます。

モチベーション

提案仕様のなかで、DNSのエラーが起こり、再帰サーバのトラフィックが増大したことを説明しています。幾つかを紹介すると

  • 2018年のDNSSECのKSK Rolloverにおいて、古いトラストアンカーを使用しDNSKEYをリトライする再起リゾルバーがいて、トラフィックが増大した (参考)
  • 2021年、Verisignの研究により、REFUSEDやSERVFAILを返す権威サーバのクエリトラフィックが多いということを示した
  • 2021年 10月、Facebookの障害で権威サーバにアクセスできなくなったとき、再帰サーバでタイムアウトが発生した。.comや.netへのクエリが、7000 query/sec から 900,000 query/secに増大した (参考)

失敗の種類

この提案仕様では、失敗の種類として、以下のものについて言及しています

  • SERVFAILの応答
  • REFUSEDの応答
  • 要求のタイムアウト
  • 委譲のループ (NSレコードの指定先がループしている)
  • エイリアスのループ (CNAMEレコードの指定先がループしている)
  • DNSSECの検証失敗

Twitchの動画配信プロトコルWarpのデモを動かしてみる

TwitchはWebTransport over HTTP/3 で単方向のライブストリーミングを可能にするWarpというプロトコル開発してます。
古い記事ですが、以前紹介した通りで、現在IETFで議論が進められています。
asnokaze.hatenablog.com

その Warpのデモコードが公開されたので動かすところまでやる。
github.com

とはいえ書いてある通りやるだけ。僕は、サーバとブラウザを別環境で動かす都合でちょっと弄っている。先に結果だけ書くと

結果

ブラウザで動画が流れる事を確認しました

また、serverを改造してkeylogfileを出力させ、パケットを復号させると

  • SETTINGSフレームで次の値を投げている
    • SETTINGS_WEBTRANS_DRAFT00 = 0x2b603742,
    • SETTINGS_H3_DATAGRAM_DRAFT04 = 0xffd277,
  • サーバからの単方向ストリーム (WebTransport stream=0x54)でデータを転送していることがわかります
    • データの最初にwarp用のjsonが格納されてます

( パケロス時に再送不要なDATAGRAMフレームも仕様上使えるが、まだ使ってなさそう

以下は手順

前準備

今回はUbuntuで動かしたが、ffmpegやnodejsはaptで降ってくるバージョンだと動かないので適宜新しいものを入れる (ffmpegはsnapでいれて、nodejsはv16を入れた)

コードの取得

$ git clone https://github.com/kixelated/warp-demo.git
$ cd ./warp-demo

流す動画ファイルの取得、変換

$ wget http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -O media/combined.mp4
$ ffmpeg -i media/combined.mp4 -f dash -use_timeline 0 -r:v 24 -g:v 48 -keyint_min:v 48 -sc_threshold:v 0 -tune zerolatency -streaming 1 -ldash 1 -seg_duration 2 -frag_duration 0.01 -frag_type duration media/fragmented.mpd

証明書の準備

$ ./cert/generate
$ ./cert/fingerprint
sB8TJSHn195H3GJm/6WoibD/OGN5M+w4x7a7RpirLo4=

フィンガープリントは後で使う

/ect/hostsの設定

echo '127.0.0.1 localhost.warp.demo' | sudo tee -a /etc/hosts

サーバの起動

Webサーバ、https://localhost.warp.demo:4444/ で動作する。アクセスすると動画プレーヤーが表示される
( 127.0.0.1でLISTENするので、外からアクセスする場合は ./player/package.json で起動を 0.0.0.0 に指定すれば良い

$ cd player
$ yarn install
$ yarn serve

warpサーバ、https://localhost.warp.demo:4443/ で動作する

$ cd server
$ go run ./warp-server

Chromeでアクセスする

先述の通り、warpサーバを起動したとは別のPCからアクセスするので
localhost.warp.demo を該当サーバのIPに向ける必要がある。/etc/hostsで指定してやれば良い

なお、現状はChrome Canaryを使う必要がある。
で、Chromeを起動時に次のオプションを渡す (起動コマンドは chrome://version で確認できる)

--origin-to-force-quic-on="localhost.warp.demo:4443" --ignore-certificate-errors-spki-list="sB8TJSHn195H3GJm/6WoibD/OGN5M+w4x7a7RpirLo4=" https://localhost.warp.demo:4444

https://localhost.warp.demo:4444/にアクセスする

HTTPのやり取りの最中に付帯情報を送れるMETADATAフレーム

METADATA frame for HTTP/2 and HTTP/3」という提案仕様がIETFに提出されている。

これは、HTTPリクエストやレスポンスをやるとりする際に、HTTPメッセージ(ヘッダやコンテンツ)とは別に追加情報を送れるようにします。追加の情報を送るのに、HTTP/2及びHTTP/3の拡張フレームとしてMETADATAフレームを定義します。METADATAフレームはストリーム上で任意のタイミングで送れるため、HTTPリクエストを送受信してる途中, HTTPレスポンスを送受信してる途中に何度も送信できます。

追加の情報の例としては、「CPUコスト」「負荷分散情報」「統計情報」などを送ることが述べられています。他にも、デバッグ情報など送るのに良さそうだなと思います。これらはHTTPセマンティクスではないので、ヘッダとして解釈されるとかはないです。

この機能はenvoyに組み込まれているしくみで、他にもGoogle QUCHEなどすでに実装しています。(具体的にどう使うのか知ってる方が居たら教えてください...)
github.com

HTTP/2の場合

SETTINGSパラーメタのSETTINGS_ENABLE_METADATAを有効にした場合に使えます。

HTTP/2ではメタデータを付加したいHTTPメッセージのストリームIDを指定します。また、メタデータはキーバリュー形式のデータであり、HPACKのフォーマットで格納します。


HTTP/3の場合

SETTINGSパラーメタのSETTINGS_ENABLE_METADATAを有効にした場合に使えます。

HTTP/3ではメタデータを付加したいHTTPメッセージと同じQUICストリーム上で送信します。また、メタデータはキーバリュー形式のデータであり、QPACKのフォーマットで格納します。

HTTP/3のCONNECT-UDPを利用したWebRTC通信

Proxying Listener UDP in HTTP」という提案仕様がIETFに提出されている。

これは、HTTP/3のCONNECT-UDPを介してWebRTC通信を可能にするための提案である。まだ議論の呼び水と鳴るdraftであるため、ここから仕様は大きく変わると思うが、ざっと眺めていく。

HTTP/3のCONNECT-UDP

本論に入る前に、まずCONNECT-UDPについて説明します。

IETFではすでに「Proxying UDP in HTTP」という仕様が議論されている。これが通称CONNECT-UDPと呼ばれているものである。実は、AppleのPrivate Relayでもすでに使用されているものである。

これは、Proxyと確立したHTTP/3コネクションをトンネリングしてUDPパケットを中継させる機能です。

この通信は第三者からはただのHTTP/3通信としてか観測できないという特徴があります。確立した通信路を通して自由なUDPパケットを中継させることが出来ます。

このCONNECT-UDPでは、H3-Datagramという仕様でUDPパケットを扱います。詳しくは以前書いた記事を参照ください。
asnokaze.hatenablog.com

Proxying Listener UDP in HTTP

通常のCONNECT-UDPではProxyを中継して通信する相手は1つでしたが、WebRTCはSTUNと通信した上で、通信相手のブラウザからもパケットを受け付ける必要があります。そこで、既存のCONNECT-UDP拡張機能として「Proxying Listener UDP in HTTP」が登場しました。

(提案者はユースケースとしてWebRTCをあげていますが、それに限定されるものでは有りません)

主な通信の流れ

上記の図に沿って通信の流れを説明します。

CONNECTリクエストとレスポンス

まずは、ブラウザAと中継サーバとでHTTP/3のコネクションを確立したのち、中継の要望を出す。

通常のCONNECT-UDPのように、拡張CONNECTメソッドを用いる(:protocolにconnect-udpが指定される)。

connect-udp-listenヘッダを付けることで、Proxying Listener UDP in HTTP の通信であることを明示する。ヘッダの値は使用するH3-DatagramのContext-IDを指定する。

HEADERS
  :method = CONNECT
  :protocol = connect-udp
  :scheme = https
  :path = /.well-known/masque/udp/*/*/
  :authority = proxy.example.org
  connect-udp-listen = 2
  capsule-protocol = ?1
|<<

中継サーバは、200番のレスポンスを返し、CONNECTリクエストの受け入れる意志を示す。

>||
HEADERS
  :status = 200
  capsule-protocol = ?1
STUNサーバへのUDPパケットの中継

ブラウザA側は、H3-DatagramUDPパケットを中継サーバに送信する。このとき、中継先となるターゲットのIPとポートを付加して送る。

 DATAGRAM
   Quarter Stream ID = 11
   Context ID = 2
   IP Version = 4
   IP Address = 192.0.2.42
   UDP Port = 1234
   UDP Payload = Encapsulated UDP Payload

中継サーバは、指定されたIP宛にUDPパケットを中継する。

中継サーバはSTUNサーバから応答となるUDPパケットを受け取ると、ブラウザA側に中継する。このときもH3-DatagramでUDPパケットを中継することになる。

このときのH3-Datagramでは、どこからきたUDPパケットなのかが付加されている。

DATAGRAM
  Quarter Stream ID = 11
  Context ID = 2
  IP Version = 4
  IP Address = 192.0.2.42
  UDP Port = 1234
  UDP Payload = Encapsulated UDP Payload

ブラウザBからのUDPパケットの中継

STUNとのやり取りが終わって、ブラウザAとブラウザBがやりとりを開始します。ブラウザBから送られるUDPパケットも、中継サーバがブラウザAに中継を行います。このときもH3-DatagramでUDPパケットを中継することになる。

DATAGRAM
  Quarter Stream ID = 11
  Context ID = 2
  IP Version = 4
  IP Address = 203.0.113.33
  UDP Port = 4321
  UDP Payload = Encapsulated UDP Payload

一般ユーザに払い出すと危険なサブドメインやメールアドレス

ユーザに対して、そのユーザ名のサブドメインやメールアドレスを払い出すWebサービスがあります。

しかし、特定のサブドメインやメールアドレスは特別な用途で使われているものもあります。そのようなサブドメインやメールアドレスを一般ユーザに払い出してしまうと危険です。

現在、IETFでは仕様上利用用途が決められている、それらのラベルをとりまとめる「Dangerous Labels in DNS and E-mail」というdraftが提出されています。

今回はそれを眺めていきます。

(あくまでIETFの取り組みであり、仕様上定義されているものをとりまとめています。クラウドサービスや特定ベンダーで特別利用しているものは現在含まれていません。)

サブドメイン

ここでとりあげるサブドメインは、利用用途が決まってるため一般ユーザに払い出すべきではありません。(例: mta-sts.example.com)

(なお、_example のように、アンダースコアで始まるものはRFC 8552ですでにまとめられています。ここでは紹介はしません。)

  • mta-sts: RFC8461で定義される、SMTP MTA Strict Transport Securityポリシーファイルの設置に利用
  • openpgpkey: 「OpenPGP Web Key Directory」でOpenPGP certificateの参照と検証に利用
  • root-key-sentinel-is-ta-(key-tag): RFC8509で、信頼するDNSSECルートキーを示すのに利用
  • root-key-sentinel-not-ta-(key-tag): RFC8509で、信頼しないDNSSECルートキーを示すのに利用
  • www: ブラウザがWebサイトが動作していると想定して利用する

メールアドレス

ここでとりあげる文字列は、利用用途が決まってるため、メールアドレスのローカルパートとして一般ユーザに払い出すべきではありません。(例: webmaster@example.com)
(なお、大文字小文字は区別しません)

  • abuse: RFC2142で、不正利用時の連絡先として指定
  • administrator: 証明書の発行用途で利用 (CA/B Forum)
  • admin: 証明書の発行用途で利用 (CA/B Forum)
  • hostmaster: RFC2142及び、証明書の発行用途で利用 (CA/B Forum)
  • info: 証明書の発行用途で利用 (参考)
  • is: 証明書の発行用途で利用 (参考)
  • it: 証明書の発行用途で利用 (参考)
  • noc: RFC2142で、ネットワークに関する連絡先として指定
  • postmaster: RFC2142及び、証明書の発行用途で利用 (CA/B Forum)
  • root: システムの問い合わせや、証明書の発行に利用?
  • security: RFC2142で、セキュリティ関係の連絡先として指定
  • ssladministrator: 証明書の発行用途で利用(参考)
  • ssladmin: 証明書の発行用途で利用 (参考)
  • sslwebmaster: 証明書の発行用途で利用(参考)
  • sysadmin: 証明書の発行用途で利用(参考)
  • webmaster: Web関連の問い合わせ先及び、証明書の発行用途で利用 (CA/B Forum)
  • www: webmasterのエイリアスとして使用

CA Browser ForumのBaseline Requirementsは、証明書発行に関するガイドラインです。CAやクラウドサービスでも、メールアドレスベースの検証ではこれらに準拠しており、これらのメールアドレスを検証に用いてますね。

おわり

もちろん今回は仕様で利用用途が定義されているものにフォーカスしています。

他にも危険なものとかありそうだけど、まとまってるのかな?

デフォルトでCookieをオリジンに紐づける、ChromeのOrigin-Bound Cookies

Blinkの開発者メーリングリストで「Intent to Prototype: Origin-Bound Cookies」という議論が行われています。

Cookieをより安全にするために、デフォルトでCookieをオリジンに紐づけるようにする提案です。Cookieをset-cookieしたオリジン以外からは、そのCookieにアクセスできないようになります。

詳しい背景や仕組みについては次のページから確認できます。
github.com

かんたんに、Origin-Bound Cookiesの動作をみていきましょう。

オリジンは、スキーム、ホスト名、ポートの組です。それらのうち一つでも異なれば、オリジンが異なることになります。

例えば、次のようになります。

オプトアウト

議論されているOrigin-Bound Cookiesでは、デフォルトの動作を変更します。オリジンを超えてcookieを共有するために、オプトアウトする仕組みが提供されています。

domain属性を付与することで、オプトアウトできます。オプトアウトすることで、異なるホスト名、ポートとcookieを共有することが出来ます。ただし、異なるスキームに共有することは出来ません。

例えば、https://example.comからセットされたcookieでも「Domain=example.com」が設定されていてれば、https://example.com:1234https://sub.example.comにはcookieが送信されます。ただし、スキームが異なるhttp://example.comには共有されません。