HTTPSに自動で切り替えるChromeのHTTPS Upgradeについて

Chromeではバージョン115 (6月リリース予定)で、『HTTPS Upgrade』という機能が導入される予定です。これは、ナビゲーション時にHTTPからHTTPSに自動でアップグレードするものです。
chromestatus.com

その動作についてドキュメントを読む

背景

HTTPS Upgrade』を導入する背景としては

多くのWebサイトがHTTPSに対応していますが、いくつかのケースでHTTPのアクセスがあり、それらを保護するためです。

HTTPSをサポートしているページでもHTTPをリッスンしている場合は、次のケースでHTTPアクセスが発生します

  • HSTS Preloadを登録していないケース
  • HSTSを利用していても、初回のアクセスするケース
  • HSTSを利用しておらず、HTTPをHTTPSにリダイレクトしないケース

動作

一言でその動作を表現すれば、『ナビゲーションの際に自動でHTTPからHTTPSに切り替える』というものです。もちろん、HTTPSでの接続に失敗した場合は、HTTPにフォールバックします。

細かい仕様については、whatwgのfetchの仕様にプルリクを確認することが出来ます
github.com

幾つかの具体動作
  • サブリソース: 今回の変更の影響は受けない。(ユーザエージェントによりmixed contentの自動Upgradeするものもある)
  • URLバーナビゲーション: http:// が指定された場合は自動Upgradeしない
  • Javascriptナビゲーション: window.locationなどによるナビゲーションは自動Upgradeの対象
  • POSTリクエスト: FormなどによりPOSTリクエストによるページ遷移は対象外 (mixed contentの対象ではある)
  • リダイレクトループ: リダイレクト処理は自動Upgradeの対象で、HTTPからHTTPSにアップグレードされるが、リダイレクトループするばあいはフォールバックしてhttpでアクセスする。

試す

Chromeバージョン111より、chrome://flags のページから下記を有効にすると試せる

認証エンドポイントを秘匿する HTTP Unprompted Authentication の仕様

HTTP Unprompted Authentication』という提案仕様がGoogleのDavid Schinazi氏らによって提出されている。

この仕様は、WebサーバにおいてHTTP認証を行っている事を秘匿するための仕様です。これによりWebサーバ上に管理者向けエンドポイントや、VPNサービスなどが動いてることを隠すことができます。

そのために必要なこととして、もちろん通信の暗号化も必要ですが、さらに正規ユーザでない第三者が認証用エンドポイントにリクエストしても「401 Authorization Required」で応答しないという要件があります。「401 Authorization Required」を返さないため、認証に使うNonceを別途共有する必要が出てきます。

HTTP Unprompted Authentication』はそのための仕組みを提供します。

Nonceの生成

この仕様では通信はTLSもしくはQUICを利用している事を前提としています。
Nonceの生成には、『RFC 5705 Keying Material Exporters for TLS』を利用しセッションに固有な秘密値を得ます。これをNonceとして利用します。

なお、ラベルには認証方式にあわせて次のどちらかを使用します

  • EXPORTER-HTTP-Unprompted-Authentication-Signature
  • EXPORTER-HTTP-Unprompted-Authentication-HMAC

認証用ヘッダ

認証用ヘッダとして新しくUnprompted-Authenticationヘッダを定義します。

Signatureで認証する場合の例

Unprompted-Authentication: Signature k=:YmFzZW1lbnQ=:;s=7;
  p=:SW5zZXJ0IHNpZ25hdHVyZSBvZiBub25jZSBoZXJlIHdo
  aWNoIHRha2VzIDUxMiBiaXRzIGZvciBFZDI1NTE5IQ==:

HMACで認証する場合の例

Unprompted-Authentication: HMAC k="YmFzZW1lbnQ=";h=6;
  p="SW5zZXJ0IEhNQUMgb2Ygbm9uY2UgaGVyZSB3aGljaCB0YWtl
  cyA1MTIgYml0cyBmb3IgU0hBLTUxMiEhISEhIQ=="

それぞれヘッダのパラメータの意味は次のとおりです

  • k: クライアントが認証に用いた鍵の識別子
  • p: 鍵情報の所持を証明するためのデータ
  • s: pを導出するのにつかった署名アルゴリズムの番号(TLSのIANAで定義される)
  • h: pを導出するのにつかったハッシュアルゴリズムの番号(TLSのIANAで定義される)

動作

クライアントは、そこに認証用エンドポイントがあるという知識を持って認証用エンドポイントにアクセスします。そのときにUnprompted-Authenticationを送信することで認証を試みます。

サーバは、送られてきたUnprompted-Authenticationヘッダを持ってユーザを認証します。認証が通れば200を返すことになります。それ以外の場合は、認証エンドポイントを秘匿するために404で応答します。

再送はしてもらう Reliable QUIC Stream Resets の仕様

Reliable QUIC Stream Resets』という提案仕様が提出されています。

もともと、RFC 9000 QUICではRESET_STREAMフレームを送信しストリームを中断するとそのストリームではデータは再送されません。『Reliable QUIC Stream Resets』ではストリームを終了しつつもパケロスしたデータは再送するように指示できます。

ユースケースとして挙げられているのは、WebTransportでのユースケースです。WebTransportではストリームの最初にHTTPリクエストと紐づけるためのセッションIDを持ちます。リセットはしつつも、パケロスしたセッションIDは再送してもらいプロトコルを利用するアプリケーション側にその情報を通知したいです。

特に難しいことはないですが、『Reliable QUIC Stream Resets』で定義されるRELIABLE_RESET_STREAMフレームを見ていきます。

Reliable QUIC Stream Resets

この仕様では、新しくRELIABLE_RESET_STREAMフレームを定義します。これはReliable Sizeを除き、RFC 9000 QUICのRESET_STREAMフレームと同様です。
(2023年7月追記: 最新draftでは、RELIABLE_RESET_STREAMフレームはCLOSE_STREAMフレームに改称されいています)


Reliable Sizeは再送して欲しいデータのオフセットを指定します。

DNSを使ってTLSハンドシェイクを高速化するZTLSについて

ZTLS: A DNS-based Approach to Zero Round Trip Delay in TLS」という論文が公開されている。アイデアが面白いので簡単に眺める。

PDFも今のところACMのサイトから見れる
https://dl.acm.org/doi/abs/10.1145/3543507.3583516

概要

  • DNSからTLSハンドシェイクに必要な情報を通知し、0-RTTハンドシェイクを行う
  • 通常のTLSと互換性がある

シーケンス図


(引用: 「ZTLS: A DNS-based Approach to Zero Round Trip Delay in TLS」 Figure 3)

  • 事前に、サーバ側はZ-Data(ハンドシェイクに必要な情報)をDNSにアップロードしておく
  • クライアントはサーバDNSに対してAレコードと、Z-Dataを含むレコードを並列に問い合わせ取得する
  • クライアントは、取得したZ-Dataに含まれるデータと自身のClientHelloの情報を元にセッションキーを生成する。その鍵でアプリケーションを暗号化し、Encrypted App Dataとして送信する。あわせてztls拡張として使用したZ-Dataのバージョンをサーバに通知する。
  • サーバは受信したClientHelloにztls拡張がなければ通常のTLSハンドシェイクにフォールバックする。ztls拡張があれば、そこに含まれるZ-Dataバージョンを元にクライアントが使ったZ-Dataを手元に用意します。そこからセッションキーを導出し、Encrypted App Dataを復号します。それ以外は、ほぼ通常のTLSと同じようにServerHelloを応答します。(Certificateは省略可能)


Z-Dataには、本Z-Dataのバージョン、本Z-Dataの有効期限、DH グループ、DH public key、証明書が格納されています。

有効性

詳しくは論文に記載のとおりだが、実際にその有効性についても実験を行っている。
アジアの各AWSリージョンにインスタンスを起動し、実際に1-RTT分早くなっている事を確認している。

感想

これって、中間者攻撃に対して初回のEncrypted App Data保護できてるのかな... よく分かってない

HTTP上でL2VPNを実現する Proxying Ethernet in HTTP について

Proxying Ethernet in HTTP』という仕様がGoogleのAlejandro R Sedeño氏から提出されています。これは、HTTP上でイーサネットフレームを送受信させるための仕様です。

背景として、IETFでは、Masque WGにおいてHTTPコネクション上で通信をトンネリングする仕組みの標準化を行っています。

すでに標準化が進められている、上記の仕様に続きイーサネットフレームを取り扱えるようにするというのが今回の提案です。ユースケースについては、L2VPNを実現するのに利用する例が挙げられています。

Proxying Ethernet in HTTPの概要

Proxying Ethernet in HTTP』では、"RFC 9297 HTTP Datagrams "を利用し、HTTP/1.1~HTTP/3でイーサネットフレームをトンネリング出来るようにします。

大まかな通信の流れは、"RFC 9298 Proxying UDP in HTTP" などと同様、拡張CONNECTメソッドでconnect-ethernetネゴシエーションし、HTTP DATAGRAMを利用しデータをトンネリングさせます。

通信例


draftに記載のL2VPN通信路の確立について説明します

  • HTTP/3コネクション確立時に、拡張CONNECT及びHTTP DATAGRAMを利用するためのSETTINGSパラメータを交換しておきます
  • クライアントは拡張CONNECTでconnect-ethernetの利用を要求します
  • Proxyはもしconnect-ethernetの通信を受け入れる場合は200で返します
  • その後は、HTTP DATAGRAMにイーサネットフレームを格納して送受信します。なお、DATAGRAMフレームには拡張CONNECTを使ったストリームと紐づけるためのコンテキストが付与されます

認証関連のリンクを示す『Link relationship types for authentication』

Link relationship types for authentication」という仕様が、IETFのHTTPAPI WGで議論されています。

例を見るのがわかりやすいと思います。

authenticate

aリンクで、リンク先をログインページということを示すことが出来るようになります。コレに対してブラウザは、ログインをナビゲーションするUIを表示したりできるようになります。

<a href="/login" rel="authenticate">Login</a>
authenticated-as

authenticated-as を使うことで、認証されたユーザの情報を取得できるリンクを示します。自身が誰として認証されているかわかります。

これは、特にAPIコールするときに有用です。自身がどの権限でAPI叩いてるかがわかるリンクを取得できるようになります。

下記はレスポンスヘッダで、認証されたユーザのユーザ情報のリンクを通知する例です

Link: <https://api.example.org/users/123-abc>; rel="authenticated-as"
logout

rel="logout" でログアウト用のリンクを示すこと出来ます。

register-user

rel="register-user" ではユーザを登録するリンクを示すことが出来ます。

状況

特にクライアントでの実装例や、サービスでの利用例は具体的には共有されてないです

ChromeのIsolated Web AppsでTCPソケットをリッスンする

以前紹介したように、ブラウザでTCPソケットを扱う『Direct Sockets API』という仕様があります。

asnokaze.hatenablog.com

前回紹介したときから時間はたち、Isolated Web Appsでの利用に限定されたり、TCPサーバソケットをリッスンできるようになったりしている。

Isolated Web Apps

Isolated Web Appsは、Web技術を用いて作られたアプリケーションを独立した環境で動作させる仕組みです。現在、フラグ付きでChromeを起動することで動作させることが出来ます。
github.com

アプリケーションの例として、Telnetクライアントを動作させる Isolated Web Apps が公開されています。
github.com

Webアプリケーションに署名し、サーバを起動させた、フラグ付きで起動したChromeで動作確認できます(詳しくはリポジトリ参照)

Chromeの起動はこんな感じです。

google-chrome-unstable --enable-features=IsolatedWebApps,IsolatedWebAppDevMode \
                       --install-isolated-web-app-from-url=http://localhost:8080

その後、"chrome://apps/" を開くと該当アプリが確認できます。


Direct Sockets API

Direct Sockets APITCPソケットやUDPソケットを操作するAPIを定義しています。現在はIsolated Web Apps環境でのみ動作させることが出来ます。
wicg.github.io

今回、TCPサーバソケットをリッスンできるようになったので軽く動作確認 (現状 reader しか無さそう)。
環境準備は面倒くさいので、上記のIsolated Web Appsを起動しデベロッパーツール経由でスクリプトを実行します。

こんな感じで実行すると、該当ポートでちゃんとリッスンされました。

const socket = new TCPServerSocket("0.0.0.0", {localPort:10083})
const { readable } = await socket.opened;
const reader = readable.getReader();

while (true) {
  debug = await reader.read();
  console.log(debug)
}

(valueがうまく取れなかったけど、、なんかおかしい?)

ひとまず、telnetで適当に文字を打って、wiresharkで見るとちゃんとリッスンしてAckを返せてるところまでは確認できた。

その他

この仕様についてはセキュリティ上の懸念事項は多くあります。何かあれば本家で語られているQ&Aを拝見してください
https://github.com/WICG/direct-sockets/blob/main/docs/explainer.md#security-considerations