この記事は ゆるTLS Advent Calendar 2021 の5日目の記事です。
ALPACA攻撃、TLS拡張と話が進んできたので、今日はSNIの話を。
[Server Name Indication]
Server Name Indication(SNI)は、2003年にまとめられた最初の6つのTLS拡張のうちのひとつである。
拡張種別はserver_name(0)
となっており、以下のServerNameList
を拡張データに含める。ServerNameList
と複数のServerName
が入りそうなフィールド名になっているが、2011年に更新されたRFC6066では、同じNameTypeのホスト名は2つ以上含めてはならないと明記されている。
struct { NameType name_type; select (name_type) { case host_name: HostName; } name; } ServerName; enum { host_name(0), (255) } NameType; opaque HostName<1..2^16-1>; struct { ServerName server_name_list<1..2^16-1> } ServerNameList;
Wiresharkでデータを見ると「www.google.co.jp」のようにServerNameが指定されているのが分かる。
このようにSNIを利用してクライアントがドメイン名を指定することで、サーバ側は1つのIPアドレスで複数のドメインのサイト(仮想サーバ)を運用している場合でも、適切なサーバ証明書をクライアントに提示できる。
ALPACA攻撃で中間者によってHTTPS通信が別のサブドメインへのFTPS通信に変換された場合には、SNIで示されるホスト名が異なるため、サーバ側でこれを検知して通信を中断すべしとされている。
[SNIとプログラミング言語]
SNIは2000年代中盤に登場した比較的新しい仕様のため、それ以前の製品では対応していないものもある*1。
自分が仕事でよく利用するJavaは、SNIより10年ほど前に登場しており、当然初期のバージョンではSNIに対応していない。
仕様策定からかなり経って、2011年7月にリリースされたJava 7でSNIに対応した。
調べて見ると、Rubyも同じくらいの時期にSNIに対応したようだ。
言語によっては自前の実装を持たず、PythonなどのようにOpenSSLに依存しているものもある。Wikipediaによると、OpenSSLは比較的早く2007年にSNIを正式サポートしたようだ。
[Encrypted SNIあるいはEncrypted ClientHello]
先の画像で見たように、SNIのデータは暗号化されていないため、通信を傍受されると、どのドメインにアクセスしようとしているかが明らかになってしまう。
そうしたプライバシーの問題への対応として提案されているのがEncrypted SNI(ESNI)である。
ENSIは、2018年に最初のドラフトが公開された。最新のドラフトは2021年8月に公開されている。
このドラフトで提案されているのは、SNIのみの暗号化だけではない。
ClientHelloメッセージそのものも暗号化して、TLS拡張として本来のClientHelloメッセージに含めて送信するEncrypted ClientHello(ECH)という仕様が提案されている。
暗号化に利用される鍵はDNS経由で渡されるらしい。
ブラウザの対応状況はというと、Firefox 85以降がECHに対応している。
Google Chromeでは開発中(Started)のようだ。Safariはよくわからなかったが、Wiresharkでパケットを見るとencrypted_client_hello拡張が見当たらなかったので、少なくともデフォルトではECHを利用できなさそうだった。
HTTPSリクエストの接続先ホスト名が暗号化されると、困る方々もいるらしい。2020年には、中国のGFWではESNIを利用していると接続できなくなるとのニュースがあった。
[まとめ]
- SNIは仮想サーバに安全に接続するためのTLS拡張
- SNIは暗号化されていないためプライバシー上の懸念がある
- その懸念を払拭すべくESNI/ECHの仕様が標準化中
*1:『プロフェッショナルSSL/TLS』55ページによると、Windows XPや初期のAndroidはSNIに対応していない。『プロフェッショナルSSL/TLS』読書メモ