#このページで確認できること

実装上の制約: 本ラボのローカル nginx 例は平文HTTPのため、TLS の検証は自前の証明書を設定するか、HTTPS で提供されるホストに対して行ってください。TLS 1.0 / 1.1 は利用非推奨(RFC 8996)のため扱いません。

ECHは標準化されていますが、実際に利用されるのは、クライアントとサーバーが対応し、ECH設定が配布され、ハンドシェイクで受理された場合です。対応の有無だけで利用を断定せず、実際の接続を観測してください。

#ハンドシェイク — 1.2 と 1.3 の違い

TLS 1.3の初回ハンドシェイクは、HelloRetryRequestが必要ない代表的なケースで1往復で完了し、証明書を含むサーバー側パラメータの大半を暗号化します。証明書ベースのTLS 1.2フルハンドシェイクで送られるサーバー証明書は受動キャプチャで読めますが、TLS 1.3の証明書はキーログ等なしの受動キャプチャでは読めません。再開セッションやPSKなど、証明書自体を送らないハンドシェイクは別です。

TLS 1.2 — 証明書 + ECDHE の代表例。証明書は平文で見える
participants: Client, Server
Client -> Server: ClientHello (SNI, cipher suites)
Server --> Client: ServerHello + Certificate (平文)
Server --> Client: ServerKeyExchange + ServerHelloDone
Client -> Server: ClientKeyExchange + ChangeCipherSpec + Finished
Server --> Client: ChangeCipherSpec + Finished
note: ここから暗号化されたアプリケーションデータ
Client -> Server: Application Data (HTTP request)
TLS 1.3 — 1往復。ECHを使わない代表例
participants: Client, Server
Client -> Server: ClientHello (SNI, key_share, ALPN)
Server --> Client: ServerHello (key_share)
Server --> Client: EncryptedExtensions + Certificate + Finished (暗号化)
Client -> Server: Finished
note: この例は 1 RTT。再接続でPSKとサーバー許可等の条件を満たせば0-RTTも可能
Client -> Server: Application Data (HTTP request)

#平文で見える情報

フィールドTLS 1.2TLS 1.3
SNI (server_name)平文ECH未使用時は平文。ECH利用時は実際のSNIをClientHelloInnerで暗号化
ALPN (クライアント提示)平文ECH未使用時は平文。ECH利用時は実際の候補をClientHelloInnerで暗号化可能
ALPN (サーバー選択)平文暗号化 (EncryptedExtensions 内)
サーバー証明書証明書ベースのフルハンドシェイクで送られる場合は平文送られる場合は暗号化
cipher suites 候補平文ClientHelloOuterの候補は平文。ECH利用時のClientHelloInnerは暗号化

ECHを使わない接続ではSNIが平文のため、プロキシ・SWG・ファイアウォールが復号せずに宛先ホスト名でフィルタリングできる場合があります。ECHが受理された接続では実際のSNIとALPN候補が保護されるため、ClientHelloOuterに見える公開名だけで同じ判断はできません。またTLS 1.3のサーバー証明書は、キーログ等のない受動キャプチャからは確認できません。

#検証コマンド

openssl — 証明書チェーンとネゴシエーション結果
$ openssl s_client -connect example.com:443 -servername example.com </dev/null
# Certificate chain / Protocol / Cipher を確認する

$ openssl s_client -connect example.com:443 -tls1_2 </dev/null 2>/dev/null | grep Protocol
# バージョンを固定して、サーバーが受け付けるかを確認する

$ openssl s_client -connect example.com:443 </dev/null 2>/dev/null \
    | openssl x509 -noout -issuer -subject -dates
# Issuer / Subject / 有効期限だけを取り出す
curl — バージョン指定と証明書情報
$ curl -v --tlsv1.3 --tls-max 1.3 https://example.com/ -o /dev/null 2>&1 | grep -E 'SSL connection|issuer:'
$ curl -v --tls-max 1.2 https://example.com/ -o /dev/null 2>&1 | grep 'SSL connection'
出力例 (証明書チェーンとcipherは時期・環境で変わる)
Certificate chain 0 s:CN=example.com i:CN=<issuing CA — 実際の出力を確認> --- New, TLSv1.3, Cipher is <negotiated cipher> Protocol : TLSv1.3
インスペクション検証の基本手順: direct と装置経由で openssl s_client の Issuer を比較します。経由時に Issuer が社内CA(装置のCA)に変わっていれば、そのセッションは復号されています。あわせて Protocol / Cipher が装置側の設定値に固定されていないかも確認します。

#観測ポイント

curl-vSSL connection using ... 行でバージョンと cipher、issuer: 行で発行者。最低バージョンを指定する --tlsv1.3 と、上限を指定する --tls-max 1.3 を併用するとTLS 1.3に固定できる。
DevToolsSecurity タブでプロトコルバージョン・鍵交換・証明書チェーンを確認。鍵アイコン → 証明書で Issuer を確認。
Proxy従来型の明示プロキシは CONNECT host:443 でTCPトンネルを張る。CONNECT 行(平文)とその後の TLS の区別をつけて観測する。SNI ベースの判定を検証するときは、ECHの利用有無も確認する。
tcpdump / Wiresharkフィルタ: tls.handshake.type == 1 (ClientHello)、tls.handshake.extensions_server_name (SNI)。ECH利用時はClientHelloOuterと暗号化されたClientHelloInnerを区別する。暗号化部分の解析にはSSLKEYLOGFILE等の鍵情報が必要。
TLS inspectionIssuerが社内CAに変わる、TLSバージョン・cipherが装置設定に制約される、証明書の有効期間が装置生成の値になる、といった差をdirect接続と比較する。クライアント証明書認証やピンニングを使うアプリは、装置の方式によって復号例外(bypass)が必要になる場合がある。

#関連

ALPN によるプロトコル選択は HTTP/2、TLS 1.3 をトランスポートに統合した設計は QUIC を参照。UDP 上で TLS 相当を実現する DTLS は別ページで扱います。