ハクソク

世界を動かす技術を、日本語で。

RFC 9849. TLS暗号化クライアントハロー

概要

  • RFC 9849はTLSClientHelloメッセージ暗号化方式を定義
  • **Encrypted Client Hello (ECH)**拡張でクライアント情報の秘匿性向上
  • SNIやALPNなど機密情報の漏洩を防止
  • Shared ModeSplit Modeの2つの運用形態に対応
  • HPKEを用いた公開鍵暗号と詳細な構成仕様を記述

TLS Encrypted Client Hello(ECH)概要

  • TLS 1.3ではハンドシェイクの大部分が暗号化されるが、ClientHelloのSNI(Server Name Indication)など一部は平文で送信され、プライバシーリスクが残存
  • 本RFCで定義される**Encrypted Client Hello (ECH)**は、ClientHello全体をサーバ公開鍵で暗号化する新しいTLS拡張方式
  • ECHにより、SNIやALPNリストなどの機密情報がネットワーク経路上で秘匿される
  • ECHはTLS 1.3DTLS 1.3およびそれ以降のバージョンでサポート
  • IETFによる標準化、広範なレビューと合意を経て策定

ECHの運用モード

  • Shared Mode
    • サービスプロバイダが全てのドメインのオリジンサーバとして機能
    • TLS接続の終端もサービスプロバイダが担当
  • Split Mode
    • サービスプロバイダはプライベートドメインのオリジンサーバではない
    • DNSレコードはサービスプロバイダを指し、TLS接続はプロバイダ経由でオリジンサーバに中継
    • サービスプロバイダはハンドシェイクの平文部分以外の通信内容にアクセス不可
  • client-facing server(クライアント向けサーバ)とbackend server(バックエンドサーバ)という役割分担
    • Shared Modeでは同一、Split Modeでは物理的に分離

ECHの動作概要

  • サーバはECH構成情報(公開鍵とメタデータ)を公開
    • DNS経由やプリセットなど複数の配布方法に対応
  • クライアントはClientHelloInner(機密情報入り)とClientHelloOuter(外部公開用)を生成
    • ClientHelloOuterにencrypted_client_hello拡張を付与し、ClientHelloInnerを暗号化して格納
  • サーバは受信時に
    • ECH非対応または復号失敗時はClientHelloOuterでハンドシェイク継続(ECH拒否)
    • 復号成功時はClientHelloInnerをbackend serverに転送しハンドシェイク(ECH受理)
  • クライアントはサーバ応答からECHが受理されたか判定し、失敗時は再試行可能

ECHのセキュリティ・プライバシー目標

  • 同一匿名性セット内のサーバへの接続が識別不能となることが主目的
  • 既存TLS 1.3のセキュリティ特性を損なわない設計
  • DNSやIPアドレスなど他経路からの情報漏洩は、DNS over HTTPSDNS over TLS/QUIC等の併用で補完

ECH構成仕様

  • **Hybrid Public Key Encryption (HPKE)**を用いた公開鍵暗号方式を採用
  • ECH構成(ECHConfig)は以下の要素で構成
    • version:ECHバージョン識別子
    • length:構成データ長
    • contents:ECHConfigContents構造体
  • ECHConfigContentsの主なフィールド
    • key_config:HPKE公開鍵や暗号スイート情報
    • maximum_name_length:バックエンドサーバ名の最大長(パディング計算用)
    • public_name:client-facing serverのDNS名
    • extensions:拡張情報リスト
  • HpkeKeyConfigの主なフィールド
    • config_id:1バイトの構成識別子(クライアントが暗号化時に利用)
    • kem_id:HPKE KEM識別子
    • public_key:HPKE公開鍵
    • cipher_suites:KDF/AEADの組み合わせリスト
  • サーバは複数のECHConfigを優先順でリスト化して配布し、クライアントは対応可能なものを選択

ECHの導入・運用上の注意点

  • ECHの導入により利用者のプライバシーが大きく向上
  • ただし、DNSやIPアドレスでの情報漏洩対策も併用が望ましい
  • サーバのTLS構成や挙動が一致していることが匿名性セットの成立条件
  • 実装の詳細や運用上の注意点はRFC本文および関連RFC(RFC9460, RFC9848等)を参照

(必要に応じて、次のセクションや詳細仕様、実装ガイドラインなどを新たなタイトルで展開できます)

Hackerたちの意見

数ヶ月前にECHについて書いたんだけど、その時は仕様がドラフトの段階で、すでに公開が承認されてたんだ。ECHやその歴史に詳しくないなら、短いけど読んでみてね: https://www.feistyduck.com/newsletter/issue_127_encrypted_cl... それに加えて、RFC 9849の他にRFC 9848もあるよ - 「DNSサービスバインディングを使ったTLS暗号化ClientHelloのブートストラップ」: https://datatracker.ietf.org/doc/rfc9848/ 記事の中に使い方の例もあるよ。
書いてくれてありがとう、イヴァン!あなたの仕事の大ファンなんだ!今度はQualysに、ECHをサポートしてないサーバーのSSL Labsの評価をBに制限させる必要があるね。それと、HSTSやHSTS Preloadがないサーバーも一緒に。
> 記事にはその使い方の例があるよ。Goではちょっと難しいけど、そんなに複雑じゃない。私たちは2024年8月にDNS用のAndroidアプリにECHを実装したけど、今のところうまく動いてるよ。https://github.com/celzero/firestack/blob/09b26631a2eac2cf9c...
面白い機能だけど、最近ちょっとイライラしてる。特にCloudflareではデフォルトでオンになってて、無料プランでは無効にできないし、ビジネスプランが必要なんだ。これってバカげてると思うけど、問題を引き起こしてる。会社の「イントラネット」のためにスプリットDNSの設定を試してるんだけど、以前にサイトにアクセスしたことがあると、ECHが記憶されちゃうんだ。だからブラウザが試みて、最終的にはECHエラーで失敗するか、Firefoxではずっと読み込み中みたいにハングしちゃう。すごくイライラするよ。たまにうまくいくこともあれば、全然ダメなこともあるし、キャッシュをクリアしても効果なし。インコグニートモードではうまくいくこともあるけど、そうじゃないこともある。これは本当の問題じゃないけど、「イントラネット」に完全に切り替えてないから、CloudflareのWAF機能を使ってるときは特にイライラする。
スプリットDNSって本当に醜いハックだよね。デバッグが難しい問題を引き起こすばかり。
公開されて嬉しい!ESNIのドラフトの頃から追ってたんだ。インドにいた時は結構役立ったよ。Jioがランダムにウェブサイトをブロックしてたから、CloudflareがESNIのドラフトをサーバーに導入して、Firefoxもクライアント側で対応してたおかげで、SNIベースのブロックを簡単に回避できたんだ。Echの作業が進む中で、両方がESNIサポートを無効にしてた時期もあったと思うけど、今はかなり進んでるね。ECHサポート付きのフォークしたnginxを設定して、クライアント(ブラウザ)テスターを作ったこともあるよ。これでECHがHTTPSサーバーでもっとメインストリームになって、面白い設定ができるようになるといいな。ECHの面白い点は、サーバーが公開名を検証する必要がない(するかもしれない)から、クライアントは中間者(検閲者)が承認したpublic_nameを使って他のウェブサイトに接続できることなんだ。RustTLSクライアントにこれを追加しようとしてるんだけど、今がその作業を再開する良いタイミングかも。[0] https://rfc9849.mywaifu.best:3443/ [1] https://github.com/rustls/rustls/issues/2741
> インドにいた時は結構役立ったよ。Jioがランダムにウェブサイトをブロックしてたから。 Jioを使ってるなら、ECHは全く必要ないよ。ブロックはほとんど基本的なもので、暗号化DNS(DoH / DoT / DNSCrypt)やFirefoxで回避できるからね(TLS ClientHelloパケットを2つに分割する)。それに関しては: https://news.ycombinator.com/item?id=34232190
インド政府は、どうしてIPベースでトラフィックをブロックしなかったんだろう?それなら、回避するのがもっと難しくなるのに。
サーバーは、TLS証明書を持っているドメインと一致しないパブリックネームを広告することもできるんだ。例えば、example.comやnsa.govみたいに。これが仕様で許可されているかは100%確信がないけど、Chromeでは動いてる。私の理解では、この機能がないと小規模なウェブサイトのオーナーにはほとんど役に立たない。なぜなら、ECHのパブリックネーム用に別のドメインを登録する必要があるからで、それは検閲者にブロックされちゃう可能性がある。
> ECHの面白い特徴の一つは、サーバーがパブリックネームを検証する必要がない(検証することもできる)から、クライアントは中間ボックス(つまり検閲者)が承認したpublic_nameを使って他のウェブサイトに接続できることだ。RustTLSクライアントにこれを追加しようとしてるんだけど、今がその再開に良いタイミングかもしれない。これがあるからこそ、年齢確認法が合理的になるんだ。高度な親でも「18歳になるまで子供にコンピュータを使わせない」みたいな非解決策なしでは検閲が技術的に不可能になるから、残された解決策はサービス運営者に責任を負わせる法律的なものになる。結局、法律がどの管轄で追いついても検閲は受けることになるけど、マルウェア(例えば広告やトラッキングソフト)がその機能を果たすための不透明性も提供することになる。
ECHについて不思議に思うことがあるんだ:> クライアント向けのサーバー証明書を検証する際、クライアントは公開名をDNSベースの参照アイデンティティとして解釈しなければならない [RFC9525]。DNS名とIPアドレスを同じ構文に組み込むクライアント(例えば、[RFC3986]のセクション7.4や[WHATWG-IPV4])は、IPv4アドレスとして解釈される名前を拒否しなければならない。明らかにIPv6の存在を考慮していないだけでなく、なぜIPベースの証明書が明示的に除外されているの?これじゃあ、小さなサーバーには全く意味がなくて、SNIのスヌーピングからの保護を提供するためには、ホスティングを共有ホストや巨大なCDNに移行する必要があるんだ。
それは、名前をIPアドレスのように見せることはできないって言ってるんじゃないかな。つまり、構文がwww.google.com[142.250.117.139](これは私が作った構文だけど)だとしたら、142.250.117.139[142.250.117.139]のようにはできないってこと。
コロンはDNSでは有効な文字じゃないから、IPv6アドレス(私が見た全ての表記に少なくとも1つコロンが含まれてる)と混同するリスクはないよ。IPv4の場合は曖昧さがあるけどね。それに、小さいサーバーにIP証明書が必要なのはどういうこと?
IPv6アドレスは、これらの構文ではDNS名と混同されないと思うから、IPv6を考慮しなかったわけじゃなくて、問題には関係ないってことだよね。確かに、「目立たないで」技術のようなECHは、元々目立っているなら役に立たないよ。広範な監視や無差別攻撃を効果的にするためのもので、魔法の透明マントじゃないから、特定のターゲットなら守ってくれないよ。
> この仕様は、小規模なサーバーには全く意味がなくて、基本的にはSNIの盗聴から保護するために、ホスティングを共有ホストや大規模なCDNに移す必要がある。実際、サーバーにECHを設定して、public_nameを`cloudflare-ech.com`みたいに設定すれば、クライアントはOuterSNIでそれを使って接続できる。CFを使わなくても大丈夫だし、中間ボックスは実際にCFに接続していると思うかもしれない(ただし、CFはIPレンジを公開しているから、他で確認できるけど)。
(開示:私はCaddyのメンテナーです)CaddyはすでにECHをサポートしていて、DNSプラグインを使ってDNS HTTPSレコードの設定を自動化しています。詳しい技術的な情報はこちらにあります。https://caddyserver.com/docs/automatic-https#encrypted-clien...
Nginxも今はECHをサポートしてるよ、12月のリリースからね。
ここでまだ出てきてない視点だけど、ECHは今のところTLSフィンガープリンティングをボット検出信号としてほぼ無効化するんだ。Cloudflareのボット管理ツールはJA3/JA4ハッシュに大きく依存していて、ClientHelloをフィンガープリンティングしてスクレイパーと実際のブラウザを識別してる。ClientHelloが暗号化されてると、その検出レイヤー全体が崩れちゃう。行動分析やJSチャレンジはまだできるけど、現在多くの単純なボットを捕まえているHTTP前のレイヤーは消えちゃう。Cloudflareがこの問題をどう内部で扱っているのか気になるな。彼らは最大のECH採用者の一つだけど、同時に最大のボット検出ベンダーでもあるからね。自分たちのビジネスを食ってるように見えるけど、すでに検出スタックをECHにあまり依存しないようにシフトしてるのかもしれない。
Cloudflareは、実際にトラフィックを提供するために、提供するサイトのClientHelloを復号化しなければならないし、そうする必要があるんだ。CFと一緒にECHを使うってことは、彼らのECHドメインとキーを使うってことだよ。
フィンガープリンティングを防ぐわけじゃないから、誤情報を広めるのはやめて。単にあなたのISPがどのウェブサイトに接続しているかを知るのを防ぐだけだよ。
あなたがフィンガープリンティングしているドメインをコントロールしているなら、内部のClientHelloを復号化してそれに基づいてフィンガープリンティングできるよ。もしフィンガープリンティングしているドメインをコントロールしていないなら、ECHは意図通りに機能しているってことだね。単純なボットがECHを実装するのはすぐには期待できないと思うけど。ボットがcurl-impersonateをダウンロードする気もないなら、ECHフラグも通過しないだろうし。
これでSNIプロキシは終わりってことだよね?
おお、やばい。サーバーでJA4ハッシュを計算したいんだけど、これができるのはサーバーが必要なキーを持ってるからなんだよね。でも、ECHに対応してJA4をプロキシとして計算できる既製のソリューションってある?HAProxyが動くって聞いたけど、nginxのJA4モジュールは未完成だし、他にセルフホスティング用のソリューション知ってる?
TLS暗号化クライアントハロー(ECH)標準は、平文のサーバー名表示(SNI)を暗号化する別の試みだ。これは、国家支援のシステム、例えばグレートファイアウォール(GFW)によるSNIベースの検閲を回避するのに非常に役立つ。平文のSNIを暗号化する以前の試みは、暗号化サーバー名表示(ESNI)で、あまりうまくいかなかった。
これについて追ってないんだけど、ESNIの何が問題だったの?
これ、RFCとは関係ないけど、RFC 10000って何になるんだろうね。なんか、TCP over Avian Carriersとか「Oops I'm A teapot」みたいなジョークRFCが出てくることを期待してるんだ。