TLS バージョン 1.3 の概要と 1.2 との違い
2018 年 8 月に TLS バージョン 1.3 が RFC 8446 として公開されました。1.2 が 2008 年に公開されましたので、実に 10 年ぶりの新バージョンです。
バージョン 1.2 との違いは、大きく以下の 3 つです。
- セキュリティ強度の高い暗号アルゴリズムを要求
- セッションのリネゴシエーションや再開に関する脆弱性のある方式を廃止し、セッション再開については新たな方式を採用
- ネゴシエーション (handshake) シーケンスの大幅な変更
1. の暗号アルゴリズムについては、ストリーム暗号 (RC4)、CBC ブロック暗号などの問題がある古い暗号アルゴリズムを廃止し、AEAD (認証付き暗号) 方式が要求されるようになりました。
また、静的な公開鍵を使う鍵交換方式 (RSA による鍵交換や、Static DH) は廃止となりました。(デジタル署名としての RSA は引き続き有用な方式として利用可能です)
つまり鍵交換には必ず Ephemeral Diffie-Hellman (DHE) および Ephemeral Elliptic Curve Diffie-Hellman (ECDHE) のどちらかが使われることになりました。
2. については脆弱性のあったリネゴシエーションや圧縮、"Session ID" や "Session Ticket" を使ったセッション再開などが廃止となりました。厳密に言うと、Session Ticket は PSK (Pre Shared Key) と統合されました。詳細は後述します。
今回の話題のメインは "2" と "3" についてですが、まず最初の話題は "3" について。
TLS v1.3 のシーケンスの変更は、Google が開発した SPDY や QUIC の思想に影響されたものであり、さらに QUIC との融合を果たす予定の HTTP/3 へとつなげるためのものだと考えています。これらは全てセキュアかつ高速な Web 通信を目指し、最適なプロトコルを模索したプロセスでもあります。
Google は SPDY を開発した 2009 年頃から、RTT (Round Trip Time: 通信の往復時間) の影響を少なくすること (具体的にはネゴシエーションによる往復を極力少なくすること) が帯域を増やすよりも重要であることを主張してきました。SPDY や QUIC の開発、今回の TLS のシーケンス変更もその流れに沿うものです。
TLS v1.2 と v1.3 のシーケンス, Handshake の比較
シーケンスを比較すると以下のようになります。
TLS v1.3 においてはデータ送信までのネゴシエーションの往復が 1 回減っているのが分かります。また、ネゴの途中から暗号化されていること (特にサーバ証明書が (クライアント証明書も) 暗号化されていること) も大きな特徴です。
key_share とは何か?
key_share は TLS v1.3 (RFC8446) にて定められた、鍵交換方式のための extension (拡張属性) です。
TLS v1.2 までは鍵交換方式は Client Hello / Server Hello の "Cipher Suite" フィールドでネゴシエーションされ、その後、DH 公開鍵などの鍵交換の元ネタを Server Key Exchange / Client Key Exchange に乗っけて送っていました。
TLS v1.3 以降では、Client Hello / Server Hello の Supported_Groups extension で、鍵交換方式の候補を複数提示し、key_share extension でそれら候補のうちいくつかのキー元ネタ (DH 公開鍵など) も送ってしまいます。
Hello Retry Request とは何か?
TLS 1.3 では Hello Retry Request という新たなメッセージが設けられました。
今までは鍵交換は Client Hello と Server Hello のネゴシエーションの間に『方式』が決まり、その後に具体的な鍵交換が行われていましたが、1.3 からは最初の Client Hello に supported_groups と key_share で鍵交換のパラメータが入ったりします。
なので一発目は不発に終わる可能性もあります。その場合サーバは Hello Retry Request で受け入れられるネゴシエーション情報を送り、Client Hello は適切なパラメータに修正して再送します。
New Session Ticket とは何か?
パケットキャプチャをしていると、HTTP/2 の通信の後に TLS 1.3 の通信として『New Session Ticket』というパケットが表示されているの気付きます。(後ほどのパケットキャプチャを参考にして下さい。)
『New Session Ticket』は TLS のセッションの再開を行うためのものです。ですが、1.2 と 1.3 では意味合いが異なります。
TLS 1.2 までのセッション再開方式=SessionID/SessionTicket
TLS 1.2 においては RFC 5077 で拡張された『次回のセッション回復のためのチケット』を意味していました。
TLS 1.2 においては、セッション回復方法としてはもともと『セッション ID 方式』のみが規定されていましたが、この方式ではセッション ID を照合するためにサーバではキャッシュ情報をタイムアウトまで持ち続ける必要がありました。
RFC 5077 で拡張された『セッションチケット方式』の場合はサーバーのみが復号できる形でクライアントに配布します。
クライアントは次回コネクション時の Client Hello 内の『Extension: SessionTicket TLS』にこのチケットを提示し、サーバ側ではそれを読み込み、その内容に沿ったセッション回復を行います。このとき、厳密にはサーバの認証は行われませんが、サーバが正しくないとチケットを復号できないのでセッション再開は不可です。
TLS 1.3 以降のセッション再開方式=PSK
一方 TLS 1.3 においては、従来の『セッション ID 方式』と『セッションチケット方式』は廃止となりました。
代わりにセッションチケットが PSK (Pre-Shared Key) に統合され、相互認証を行いつつ、セッションが再開されるようになりました。
PSK の本来の使い方は RFC 4279 に記載がある通り、(実装はほとんど見たことないですが、) 鍵交換方式および認証方式として存在していました。すなわち、クライアントアプリケーションとサーバアプリケーションでお互いに PSK を手動でセットし、それをもって互いが正しい通信先だと解釈し (この PSK 認証方式の場合、サーバ証明書は不要となる)、さらにそれを素に共通鍵を生成するのです。
イメージとしては、IPsec の PSK と同じ使い方です。
今回の改訂により、サーバはセッション中の New Session Ticket にて PSK を払い出し、次回セッション再開時にクライアントは PSK による認証をしつつ、PSK を素にしたセッション回復 (Session Resumption)を行うことができるようになりました。つまり、今まで静的な PSK しか使えなかったのが、動的な PSK にも対応するようになったのです。
New Session Ticket メッセージは暗号化通信が開始された後であればいつ送ってもよいことになっています。
コネクションとセッションの違いとそれぞれの寿命(timeout)
TLS のコネクションは Client Hello から始まり、Alert (正常終了の場合は Alert: close_notify、異常終了の場合はそれ以外) で終了します。
なので寿命は TCP コネクションとほぼ同じで、3 way handshake と fin,ack の分だけ TLS コネクションのほうが僅かに短いです。
一方、セッションはセッションキーを維持し続けるまでの時間のことで、RFC の仕様上は 24時間以内となっていますが、実装として例えば Apache + mod_ssl は 300 秒だったりします。
SSLSessionCacheTimeout 300
この時間内であれば、クライアントとサーバはセッションキーを保持し続けますので、新たなコネクション (Handshake) を確立する際にネゴの一部が省略可能です。
TLS v1.3 の PSK を使った 1-RTT でのセッション再開
TLS v1.3 における PSK を使った 1-RTT セッション再開のシーケンスを以下に示します。
"key_share" Extension (拡張) を使って PSK を提示します。ですがこれは挨拶代わりです。
TLS v1.3 の PSK を使った 0-RTT でのセッション再開
TLS v1.3 の 0-RTT セッション再開のシーケンスを以下に示します。
1 往路目で PSK と一緒にデータも送っちゃいます。
0-RTT であることを示す early_data Extension も含まれています。
これは今話題の HTTP/3 (HTTP over QUIC) で実装されることがほぼ決まっており、TLSv1.3 作成時でこの HTTP/3 を相当意識していたものと思われます。
ただし、セキュリティ的にやや甘く、実装は任意になっています。
パケットキャプチャを覗いてみる
TLS v1.2 のパケットキャプチャ
Server Hello のパケット内の表示で "Version: TLS 1.2" となっています。16進数だと "0x0303" です。
ところが、TLS 1.3 も同じ "0x0303" になっています。
今まででは SSLv3.0=0x0300, TLSv1.0=0x0301, TLSv1.1=0x0302, TLSv1.2=0x0303 という流れでしたが、TLS v1.3 では TLS v1.2 と同じく 0x0303 となっています。これについて TLS v1.3 の RFC 8446 の Appendix.D.1 に以下のように書かれています。
A TLS 1.3 client who wishes to negotiate with servers that do not support TLS 1.3 will send a normal TLS 1.3 ClientHello containing 0x0303 (TLS 1.2) in ClientHello.legacy_version but with the correct version(s) in the "supported_versions" extension. If the server does not support TLS 1.3, it will respond with a ServerHello containing an older version number. If the client agrees to use this version, the negotiation will proceed as appropriate for the negotiated protocol.
つまり、下位互換とのスムーズなネゴのために、このフィールドには TLS v1.2 を示す 0x0303 を格納しているのです。
流れとしてはまず、Client Hello の Supported_versions に TLS v1.3 を示す 0x0304 を格納しておきます。
この Extension: supported_versions は TLS v1.3 対応のサーバしか理解できません。対応していない場合はこのフィールドを無視して、TLS v1.2 で動作します。
もしサーバ側で TLS v1.3 を理解できる場合は、ServerHello の Supported_version に 0x0304 を格納します。
ここで TLS のバージョンを識別しているのです。
なお、上記パケットキャプチャでは TLS で暗号化している部分を以下のページの手法で復号して表示しています。
コメント