<p>本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">RFC 8446 TLS 1.3における鍵交換モードの詳細</h1>
<h2 class="wp-block-heading">背景</h2>
<p>TLS (Transport Layer Security) は、インターネット上で安全な通信を確立するためのプロトコルであり、Webブラウジング (HTTPS) をはじめとする多くのアプリケーションで利用されています。以前のバージョンであるTLS 1.2には、いくつかの課題がありました。例えば、複雑な暗号スイートの選択プロセス、一部の鍵交換メカニズムにおける前方秘匿性 (Forward Secrecy) の欠如、および複数回のラウンドトリップタイム (RTT) を要するハンドシェイクによるパフォーマンスオーバーヘッドなどです。</p>
<p>これらの課題に対処するため、IETF (Internet Engineering Task Force) は、2018年8月10日に<a href="https://datatracker.ietf.org/doc/html/rfc8446">RFC 8446</a>としてTLS 1.3を標準化しました¹。TLS 1.3は、セキュリティとパフォーマンスの両面で大幅な改善をもたらし、特に鍵交換メカニズムが大きく変更されました。</p>
<h2 class="wp-block-heading">設計目標</h2>
<p>TLS 1.3の主要な設計目標は以下の通りです。</p>
<ul class="wp-block-list">
<li><p><strong>ハンドシェイクの簡素化と高速化:</strong> 従来の2-RTTフルハンドシェイクを1-RTTに短縮し、さらに0-RTT (Zero Round Trip Time) による初期データ送信を可能にすることで、接続確立までの遅延を最小化します。</p></li>
<li><p><strong>前方秘匿性の強制:</strong> すべての鍵交換モードにおいて、セッション鍵が漏洩しても過去の通信が解読されない前方秘匿性 (PFS: Perfect Forward Secrecy) を必須とします。これにより、RSA鍵交換のような、長期秘密鍵の漏洩が過去の通信の解読につながるモードは排除されました。</p></li>
<li><p><strong>脆弱な機能・暗号の排除:</strong> RC4、CBCモード暗号、MD5、SHA-1などのレガシーで脆弱な暗号アルゴリズムや、Renegotationなどのプロトコル機能を削除し、攻撃対象領域を縮小します。</p></li>
<li><p><strong>鍵生成の安全性向上:</strong> HKDF (HMAC-based Key Derivation Function) を用いて、より堅牢で一貫性のある鍵スケジュールを導入し、セッション鍵の導出プロセスを明確化します。</p></li>
</ul>
<h2 class="wp-block-heading">詳細</h2>
<p>TLS 1.3における鍵交換は、主にEphemeral Diffie-Hellman (DHE) とPre-Shared Key (PSK) の2つのモード、およびそれらの組み合わせであるPSK-DHEモードに基づいています。</p>
<h3 class="wp-block-heading">鍵交換モードの種類</h3>
<ol class="wp-block-list">
<li><p><strong>Ephemeral Diffie-Hellman (DHE) モード:</strong></p>
<ul>
<li><p>新規接続の確立に用いられます。クライアントとサーバーは、毎回異なる一時的な (ephemeral) Diffie-Hellmanパラメータを交換し、共有秘密鍵を生成します。これにより、常に前方秘匿性が確保されます。利用される楕円曲線DHE (ECDHE) グループには、P-256、P-384、P-521、X25519、X448などがあります。</p></li>
<li><p><code>https://datatracker.ietf.org/doc/html/rfc8446#section-2</code>, 2018年8月10日, IETF</p></li>
</ul></li>
<li><p><strong>Pre-Shared Key (PSK) モード:</strong></p>
<ul>
<li><p>クライアントとサーバーが事前に共有鍵 (PSK) を持っている場合に利用されます。これは主にセッション再開 (session resumption) に使用され、前回のセッションから導出されたマスターシークレットがPSKとして再利用されます。これにより、ハンドシェイクを大幅に短縮できますが、単独のPSKモードでは前方秘匿性が提供されません。</p></li>
<li><p><code>https://datatracker.ietf.org/doc/html/rfc8446#section-2</code>, 2018年8月10日, IETF</p></li>
</ul></li>
<li><p><strong>PSK-DHEモード:</strong></p>
<ul>
<li><p>PSKとDHEを組み合わせたモードです。既存のPSKを使用しつつ、DHE鍵交換も同時に行うことで、前方秘匿性を確保します。推奨される鍵交換モードであり、高速なセッション確立とセキュリティの両方を実現します。</p></li>
<li><p><code>https://datatracker.ietf.org/doc/html/rfc8446#section-2.2</code>, 2018年8月10日, IETF</p></li>
</ul></li>
</ol>
<h3 class="wp-block-heading">1-RTTフルハンドシェイク (DHEまたはPSK-DHE)</h3>
<p>TLS 1.3の標準的なフルハンドシェイクは、1-RTTで完了します。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
sequenceDiagram
participant Client
participant Server
Client ->> Server: ClientHello (KeyShare, SupportedVersions, SupportedGroups, SignatureAlgorithms)
Server ->> Client: ServerHello (SelectedVersion, SelectedGroup, KeyShare)
Server ->> Client: EncryptedExtensions (e.g., ALPN)
Server ->> Client: Certificate (Optional)
Server ->> Client: CertificateVerify (Optional)
Server ->> Client: Finished
Client ->> Server: Finished
Client ->> Server: [Application Data]
Server ->> Client: [Application Data]
</pre></div>
<ul class="wp-block-list">
<li><p><strong>ClientHello:</strong> クライアントは、サポートするTLSバージョン (TLS 1.3のみ)、DHEグループとその公開鍵シェア (<code>key_share</code> 拡張)、サポートする暗号スイート、署名アルゴリズムなどを送信します。</p></li>
<li><p><strong>ServerHello:</strong> サーバーは、選択したTLSバージョン (TLS 1.3)、選択したDHEグループとその公開鍵シェア、選択した暗号スイートなどを応答します。この時点で、クライアントとサーバーはDiffie-Hellman鍵交換を完了し、共有秘密鍵から鍵スケジューリングを通じて初期のハンドシェイクシークレットを導出します。</p></li>
<li><p><strong>EncryptedExtensions, Certificate, CertificateVerify, Finished:</strong> 以降のメッセージは、確立されたハンドシェイクシークレットによって暗号化されます。サーバーは、必要に応じて証明書とその検証を送り、自身のハンドシェイクが完了したことを示す<code>Finished</code>メッセージを送信します。</p></li>
<li><p><strong>Client Finished:</strong> クライアントも同様に<code>Finished</code>メッセージを送信し、ハンドシェイクが完了。以降、アプリケーションデータを交換します。</p></li>
</ul>
<h3 class="wp-block-heading">0-RTTハンドシェイク (Early Data)</h3>
<p>PSKモードの強化として、TLS 1.3は0-RTTハンドシェイクを導入しました。これにより、以前のセッションで確立されたPSK (resumption PSK) を利用して、ClientHelloと共に暗号化されたアプリケーションデータを送信できます。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
sequenceDiagram
participant Client
participant Server
Client ->> Server: ClientHello (PreSharedKey, EarlyData) + [Early Application Data]
Server -->> Client: (Optional: Reject Early Data)
Server ->> Client: ServerHello (SelectedVersion, SelectedGroup, KeyShare, PreSharedKey)
Server ->> Client: EncryptedExtensions
Server ->> Client: Certificate (Optional)
Server ->> Client: CertificateVerify (Optional)
Server ->> Client: Finished
Client ->> Server: Finished
Client ->> Server: [Application Data]
Server ->> Client: [Application Data]
</pre></div>
<ul class="wp-block-list">
<li><p><strong>ClientHello with Early Data:</strong> クライアントは、以前のセッションから得たPSK識別子 (<code>pre_shared_key</code> 拡張) と共に、0-RTTのアプリケーションデータを<code>early_data</code>拡張に含めて送信します。</p></li>
<li><p><strong>ServerHello:</strong> サーバーはPSKを受け入れ、DHE鍵交換も行うPSK-DHEモードで鍵を確立します。サーバーは早期データを受け入れるか拒否するかを決定し、その結果をClientHello後のメッセージで示します。</p></li>
<li><p><strong>後続処理:</strong> 以降は1-RTTフルハンドシェイクと同様に、残りのハンドシェイクメッセージを暗号化して交換し、セッションを確立します。</p></li>
</ul>
<h3 class="wp-block-heading">鍵スケジュールの概要</h3>
<p>TLS 1.3では、HKDF (HMAC-based Key Derivation Function) を用いて、すべての秘密鍵と派生鍵を体系的に生成する鍵スケジュールが導入されています。これにより、鍵の導出プロセスが明確化され、セキュリティ解析が容易になりました。ClientHello、ServerHello、Finishメッセージなど、ハンドシェイクの各段階で得られるシークレットを元に、DHE共有秘密、PSK秘密、そして最終的なアプリケーションデータ暗号化鍵などが派生されます。</p>
<h3 class="wp-block-heading">プロトコル構造例</h3>
<p>TLS 1.3のメッセージは、TLSPlaintextレコードに格納されます。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">TLSPlaintext Record
type: 8 bits (例: handshake, application_data)
legacy_record_version: 16 bits (TLS 1.3では0x0301, レガシー互換のため)
length: 16 bits (レコードの長さ、最大16384バイト + パディング)
fragment: variable (暗号化されたペイロード)
Handshake Message Header
msg_type: 8 bits (例: client_hello=1, server_hello=2, finished=20)
length: 24 bits (メッセージ本体の長さ)
body: variable
ClientHelloのkey_share拡張構造 (簡略化)
ExtensionType key_share = 0x0033
struct {
KeyShareEntry client_shares<2..2^16-1>;
} KeyShareClientHello;
struct {
NamedGroup group;
opaque key_exchange<1..2^16-1>;
} KeyShareEntry;
ClientHelloのpre_shared_key拡張構造 (簡略化)
ExtensionType pre_shared_key = 0x0029
struct {
opaque identity<1..2^16-1>;
uint32 obfuscated_ticket_age;
} PskIdentity;
struct {
opaque binder<32..255>; /* Hash of ClientHello up to the binders field */
} PskBinderEntry;
struct {
PskIdentity identities<7..2^16-1>;
PskBinderEntry binders<32..2^16-1>;
} PreSharedKeyExtension;
</pre>
</div>
<ul class="wp-block-list">
<li><p><code>legacy_record_version</code> は、後方互換性のためにTLS 1.2の値 (0x0303) を設定することが一般的ですが、TLS 1.3ではこのフィールドは無視され、<code>SupportedVersions</code>拡張が実際のバージョンを示します。</p></li>
<li><p><code>KeyShareEntry</code>は、クライアントがサポートするDHEグループとその公開鍵を含みます。</p></li>
<li><p><code>PskIdentity</code>は、クライアントが再利用したいPSKの識別子と、チケット発行からの経過時間 (<code>obfuscated_ticket_age</code>) を含みます。<code>PskBinderEntry</code>は、ClientHello全体に対するHMACであり、選択されたPSKの正当性を証明します。</p></li>
</ul>
<h2 class="wp-block-heading">相互運用性</h2>
<h3 class="wp-block-heading">TLS 1.2 との比較</h3>
<figure class="wp-block-table"><table>
<thead>
<tr>
<th style="text-align:left;">特徴</th>
<th style="text-align:left;">TLS 1.2</th>
<th style="text-align:left;">TLS 1.3 (RFC 8446)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><strong>フルハンドシェイクRTT</strong></td>
<td style="text-align:left;">2 RTT</td>
<td style="text-align:left;">1 RTT</td>
</tr>
<tr>
<td style="text-align:left;"><strong>セッション再開RTT</strong></td>
<td style="text-align:left;">1 RTT</td>
<td style="text-align:left;">0 RTT (PSK-DHEまたはPSK)</td>
</tr>
<tr>
<td style="text-align:left;"><strong>前方秘匿性 (PFS)</strong></td>
<td style="text-align:left;">オプション (DHE/ECDHE選択時のみ)</td>
<td style="text-align:left;">必須 (DHE/ECDHEのみ)</td>
</tr>
<tr>
<td style="text-align:left;"><strong>鍵交換モード</strong></td>
<td style="text-align:left;">RSA、DHE/ECDHE、PSKなど複数</td>
<td style="text-align:left;">DHE/ECDHE、PSK、PSK-DHEのみ</td>
</tr>
<tr>
<td style="text-align:left;"><strong>暗号スイート</strong></td>
<td style="text-align:left;">多くのレガシー暗号 (RC4, CBCモード, MD5, SHA-1ベースのHMACなど) を含む</td>
<td style="text-align:left;">現代的で強力な認証付き暗号 (AEAD) のみ (AES-GCM, ChaCha20-Poly1305)</td>
</tr>
<tr>
<td style="text-align:left;"><strong>プロトコルメッセージ</strong></td>
<td style="text-align:left;">ClientKeyExchangeなど、鍵交換用の追加メッセージあり</td>
<td style="text-align:left;">ClientHello/ServerHello内の拡張で鍵交換情報をやり取りし、メッセージ数を削減</td>
</tr>
<tr>
<td style="text-align:left;"><strong>ダウングレード攻撃対策</strong></td>
<td style="text-align:left;">不十分</td>
<td style="text-align:left;">ServerHelloのランダム値に特定のパターンを導入し、検知機能を強化</td>
</tr>
</tbody>
</table></figure>
<h3 class="wp-block-heading">HTTP/2 および HTTP/3 における TLS 1.3 の役割</h3>
<ul class="wp-block-list">
<li><p><strong>HTTP/2 (TCP上):</strong>
HTTP/2はTCP上で動作するため、TLS 1.3が提供する1-RTTハンドシェイクは、接続確立のレイテンシを削減し、Webパフォーマンスを向上させます。しかし、TCP層で発生するHead-of-Line (HOL) Blockingの問題は、TLS 1.3を導入しても解決されません。複数のストリームがTCPセグメントを共有するため、1つのセグメントの紛失が全てのストリームの処理をブロックする可能性があります。</p></li>
<li><p><strong>HTTP/3 (QUIC/UDP上):</strong>
HTTP/3は、TCPの代わりにUDP上で動作するQUICプロトコルを基盤としており、TLS 1.3を必須のセキュリティレイヤーとして利用します。QUICは、多重化ストリームをネイティブにサポートすることでTCPのHOL Blockingを回避します。さらに、TLS 1.3の0-RTTハンドシェイクと組み合わせることで、<strong>ほとんどの接続を0-RTTで確立</strong>でき、大幅なレイテンシ削減とユーザー体験の向上を実現します²。QUICのストリームレベルでの信頼性保証と輻輳制御は、TLS 1.3のセキュリティ層の上で機能します。</p></li>
</ul>
<h2 class="wp-block-heading">セキュリティ考慮</h2>
<p>TLS 1.3はセキュリティを大幅に強化していますが、特定の機能には注意が必要です。</p>
<ul class="wp-block-list">
<li><p><strong>前方秘匿性 (Perfect Forward Secrecy, PFS):</strong>
TLS 1.3では、すべての鍵交換でDHE/ECDHEが必須とされたため、前方秘匿性が常に保証されます。これは、長期秘密鍵 (例えばサーバーのRSA証明書鍵) が将来漏洩しても、過去の通信内容が解読されることを防ぐ上で極めて重要です。</p></li>
<li><p><strong>リプレイ攻撃 (0-RTT Early Data):</strong>
0-RTTデータは、ClientHelloと共に暗号化されて送信されるため、サーバーはクライアントの真正性を完全に検証する前にデータを受信します。これにより、攻撃者が0-RTTデータを傍受し、サーバーに繰り返し送信することで、同じ操作を複数回実行させる「リプレイ攻撃」のリスクが生じます。
<a href="https://datatracker.ietf.org/doc/html/rfc8446#section-E.5">RFC 8446のセクションE.5</a>では、サーバーは0-RTTデータを処理する際に、冪等 (idempotent) な操作(何度実行しても結果が変わらない操作)に限定するか、強力なリプレイ検出メカニズムを実装するよう強く推奨されています¹。Google Security Blogも0-RTTの高速化を強調しつつ、このリプレイ攻撃への注意を促しています³。</p></li>
<li><p><strong>ダウングレード攻撃対策:</strong>
TLS 1.3は、TLS 1.2やそれ以前のバージョンへの意図的なダウングレード攻撃を検知するメカニズムを導入しています。ServerHelloの<code>random</code>フィールドの下位8バイトを特定のパターン (<code>0xCF</code>) に設定し、<code>legacy_session_id_echo</code>が空でない場合にダウングレードが発生したことを示すことで、クライアントがこれを検知できます¹。</p></li>
<li><p><strong>鍵更新 (Key Update):</strong>
TLS 1.3は、<code>KeyUpdate</code>メッセージを通じて、確立された接続のセッション鍵を定期的に更新する機能を提供します。これにより、長期にわたる通信セッションにおいても、鍵の漏洩リスクを低減し、前方秘匿性をさらに強化します。<code>KeyUpdate</code>メッセージは、次回の送信用および受信用トラフィック鍵を更新するように、相手に要求するために使用されます¹。</p></li>
</ul>
<h2 class="wp-block-heading">実装メモ</h2>
<p>TLS 1.3の実装には、いくつかの重要な考慮事項があります。</p>
<ul class="wp-block-list">
<li><p><strong>MTU (Maximum Transmission Unit) / Path MTU:</strong>
TLSレコードレイヤーは、アプリケーションデータを複数のレコードに分割 (フラグメンテーション) することができます。しかし、ネットワークのPath MTUを超過するサイズのTLSレコードを送信すると、IPフラグメンテーションやパケットロスを引き起こし、パフォーマンスが低下する可能性があります。MTUに収まるようにTLSレコードサイズを調整し、可能な限りTCP MSS (Maximum Segment Size) やQUICのPacket sizeに適合させることが推奨されます。</p></li>
<li><p><strong>HOL blocking 回避 (Head-of-Line Blocking):</strong>
TLS 1.3はTCP上で動作する場合、TCP自身のHOL blocking問題は解決しません。これは、TCPがバイトストリームの順序保証を行うため、1つのパケット損失が後続の全てのパケットの処理をブロックするためです。HTTP/3がQUICを使用することで、この問題をアプリケーションストリームレベルで解決しています。TLS 1.3をTCP上で利用する際は、この制約を理解しておく必要があります。</p></li>
<li><p><strong>キュー制御と優先度:</strong>
特に0-RTTデータを利用する場合、そのデータの優先度を通常のアプリケーションデータとどのように扱うかが重要です。サーバー側で0-RTTデータを即座に処理するのか、それとも完全なハンドシェイクが完了するまで待機させるのか、アプリケーションの要件に応じて適切なキューイングと優先度設定が必要です。リプレイ攻撃のリスクを考慮し、0-RTTデータの処理には特に慎重な設計が求められます。</p></li>
<li><p><strong>暗号学的乱数生成器 (CSPRNG):</strong>
鍵交換のセキュリティは、使用される乱数生成器の品質に直接依存します。TLS 1.3の実装では、エフェメラル鍵ペアの生成やその他の暗号操作に、強力なCSPRNGを確実に使用することが不可欠です。</p></li>
</ul>
<h2 class="wp-block-heading">まとめ</h2>
<p>RFC 8446によって標準化されたTLS 1.3は、現代のインターネット通信におけるセキュリティとパフォーマンスの要求に応えるための重要な進化です。DHEおよびPSKを基盤とする鍵交換モード、1-RTTフルハンドシェイク、そして0-RTTハンドシェイクによる高速化は、Web体験を大きく改善します。同時に、常時前方秘匿性の強制、脆弱な暗号の排除、ダウングレード攻撃対策などにより、セキュリティも強化されました。</p>
<p>しかし、0-RTTの利便性と引き換えに生じるリプレイ攻撃のリスクは、実装者が十分に理解し、適切に対処すべき重要なセキュリティ考慮事項です。プロトコルエンジニアとして、これらの詳細を理解し、堅牢で安全なTLS 1.3の実装を設計することが求められます。</p>
<hr/>
<p>参照:</p>
<ol class="wp-block-list">
<li><p>IETF. “RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3”. 2018年8月10日. <a href="https://datatracker.ietf.org/doc/html/rfc8446">https://datatracker.ietf.org/doc/html/rfc8446</a></p></li>
<li><p>Cloudflare Blog. “A detailed look at TLS 1.3”. 2018年11月9日. <a href="https://blog.cloudflare.com/tls-1-3-overview/">https://blog.cloudflare.com/tls-1-3-overview/</a></p></li>
<li><p>Google Security Blog. “TLS 1.3 everywhere”. 2018年8月22日. <a href="https://security.googleblog.com/2018/08/tls-13-everywhere.html">https://security.googleblog.com/2018/08/tls-13-everywhere.html</a></p></li>
</ol>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
RFC 8446 TLS 1.3における鍵交換モードの詳細
背景
TLS (Transport Layer Security) は、インターネット上で安全な通信を確立するためのプロトコルであり、Webブラウジング (HTTPS) をはじめとする多くのアプリケーションで利用されています。以前のバージョンであるTLS 1.2には、いくつかの課題がありました。例えば、複雑な暗号スイートの選択プロセス、一部の鍵交換メカニズムにおける前方秘匿性 (Forward Secrecy) の欠如、および複数回のラウンドトリップタイム (RTT) を要するハンドシェイクによるパフォーマンスオーバーヘッドなどです。
これらの課題に対処するため、IETF (Internet Engineering Task Force) は、2018年8月10日にRFC 8446としてTLS 1.3を標準化しました¹。TLS 1.3は、セキュリティとパフォーマンスの両面で大幅な改善をもたらし、特に鍵交換メカニズムが大きく変更されました。
設計目標
TLS 1.3の主要な設計目標は以下の通りです。
ハンドシェイクの簡素化と高速化: 従来の2-RTTフルハンドシェイクを1-RTTに短縮し、さらに0-RTT (Zero Round Trip Time) による初期データ送信を可能にすることで、接続確立までの遅延を最小化します。
前方秘匿性の強制: すべての鍵交換モードにおいて、セッション鍵が漏洩しても過去の通信が解読されない前方秘匿性 (PFS: Perfect Forward Secrecy) を必須とします。これにより、RSA鍵交換のような、長期秘密鍵の漏洩が過去の通信の解読につながるモードは排除されました。
脆弱な機能・暗号の排除: RC4、CBCモード暗号、MD5、SHA-1などのレガシーで脆弱な暗号アルゴリズムや、Renegotationなどのプロトコル機能を削除し、攻撃対象領域を縮小します。
鍵生成の安全性向上: HKDF (HMAC-based Key Derivation Function) を用いて、より堅牢で一貫性のある鍵スケジュールを導入し、セッション鍵の導出プロセスを明確化します。
詳細
TLS 1.3における鍵交換は、主にEphemeral Diffie-Hellman (DHE) とPre-Shared Key (PSK) の2つのモード、およびそれらの組み合わせであるPSK-DHEモードに基づいています。
鍵交換モードの種類
Ephemeral Diffie-Hellman (DHE) モード:
新規接続の確立に用いられます。クライアントとサーバーは、毎回異なる一時的な (ephemeral) Diffie-Hellmanパラメータを交換し、共有秘密鍵を生成します。これにより、常に前方秘匿性が確保されます。利用される楕円曲線DHE (ECDHE) グループには、P-256、P-384、P-521、X25519、X448などがあります。
https://datatracker.ietf.org/doc/html/rfc8446#section-2, 2018年8月10日, IETF
Pre-Shared Key (PSK) モード:
クライアントとサーバーが事前に共有鍵 (PSK) を持っている場合に利用されます。これは主にセッション再開 (session resumption) に使用され、前回のセッションから導出されたマスターシークレットがPSKとして再利用されます。これにより、ハンドシェイクを大幅に短縮できますが、単独のPSKモードでは前方秘匿性が提供されません。
https://datatracker.ietf.org/doc/html/rfc8446#section-2, 2018年8月10日, IETF
PSK-DHEモード:
PSKとDHEを組み合わせたモードです。既存のPSKを使用しつつ、DHE鍵交換も同時に行うことで、前方秘匿性を確保します。推奨される鍵交換モードであり、高速なセッション確立とセキュリティの両方を実現します。
https://datatracker.ietf.org/doc/html/rfc8446#section-2.2, 2018年8月10日, IETF
1-RTTフルハンドシェイク (DHEまたはPSK-DHE)
TLS 1.3の標準的なフルハンドシェイクは、1-RTTで完了します。
sequenceDiagram
participant Client
participant Server
Client ->> Server: ClientHello (KeyShare, SupportedVersions, SupportedGroups, SignatureAlgorithms)
Server ->> Client: ServerHello (SelectedVersion, SelectedGroup, KeyShare)
Server ->> Client: EncryptedExtensions (e.g., ALPN)
Server ->> Client: Certificate (Optional)
Server ->> Client: CertificateVerify (Optional)
Server ->> Client: Finished
Client ->> Server: Finished
Client ->> Server: [Application Data]
Server ->> Client: [Application Data]
ClientHello: クライアントは、サポートするTLSバージョン (TLS 1.3のみ)、DHEグループとその公開鍵シェア (key_share 拡張)、サポートする暗号スイート、署名アルゴリズムなどを送信します。
ServerHello: サーバーは、選択したTLSバージョン (TLS 1.3)、選択したDHEグループとその公開鍵シェア、選択した暗号スイートなどを応答します。この時点で、クライアントとサーバーはDiffie-Hellman鍵交換を完了し、共有秘密鍵から鍵スケジューリングを通じて初期のハンドシェイクシークレットを導出します。
EncryptedExtensions, Certificate, CertificateVerify, Finished: 以降のメッセージは、確立されたハンドシェイクシークレットによって暗号化されます。サーバーは、必要に応じて証明書とその検証を送り、自身のハンドシェイクが完了したことを示すFinishedメッセージを送信します。
Client Finished: クライアントも同様にFinishedメッセージを送信し、ハンドシェイクが完了。以降、アプリケーションデータを交換します。
0-RTTハンドシェイク (Early Data)
PSKモードの強化として、TLS 1.3は0-RTTハンドシェイクを導入しました。これにより、以前のセッションで確立されたPSK (resumption PSK) を利用して、ClientHelloと共に暗号化されたアプリケーションデータを送信できます。
sequenceDiagram
participant Client
participant Server
Client ->> Server: ClientHello (PreSharedKey, EarlyData) + [Early Application Data]
Server -->> Client: (Optional: Reject Early Data)
Server ->> Client: ServerHello (SelectedVersion, SelectedGroup, KeyShare, PreSharedKey)
Server ->> Client: EncryptedExtensions
Server ->> Client: Certificate (Optional)
Server ->> Client: CertificateVerify (Optional)
Server ->> Client: Finished
Client ->> Server: Finished
Client ->> Server: [Application Data]
Server ->> Client: [Application Data]
ClientHello with Early Data: クライアントは、以前のセッションから得たPSK識別子 (pre_shared_key 拡張) と共に、0-RTTのアプリケーションデータをearly_data拡張に含めて送信します。
ServerHello: サーバーはPSKを受け入れ、DHE鍵交換も行うPSK-DHEモードで鍵を確立します。サーバーは早期データを受け入れるか拒否するかを決定し、その結果をClientHello後のメッセージで示します。
後続処理: 以降は1-RTTフルハンドシェイクと同様に、残りのハンドシェイクメッセージを暗号化して交換し、セッションを確立します。
鍵スケジュールの概要
TLS 1.3では、HKDF (HMAC-based Key Derivation Function) を用いて、すべての秘密鍵と派生鍵を体系的に生成する鍵スケジュールが導入されています。これにより、鍵の導出プロセスが明確化され、セキュリティ解析が容易になりました。ClientHello、ServerHello、Finishメッセージなど、ハンドシェイクの各段階で得られるシークレットを元に、DHE共有秘密、PSK秘密、そして最終的なアプリケーションデータ暗号化鍵などが派生されます。
プロトコル構造例
TLS 1.3のメッセージは、TLSPlaintextレコードに格納されます。
TLSPlaintext Record
type: 8 bits (例: handshake, application_data)
legacy_record_version: 16 bits (TLS 1.3では0x0301, レガシー互換のため)
length: 16 bits (レコードの長さ、最大16384バイト + パディング)
fragment: variable (暗号化されたペイロード)
Handshake Message Header
msg_type: 8 bits (例: client_hello=1, server_hello=2, finished=20)
length: 24 bits (メッセージ本体の長さ)
body: variable
ClientHelloのkey_share拡張構造 (簡略化)
ExtensionType key_share = 0x0033
struct {
KeyShareEntry client_shares<2..2^16-1>;
} KeyShareClientHello;
struct {
NamedGroup group;
opaque key_exchange<1..2^16-1>;
} KeyShareEntry;
ClientHelloのpre_shared_key拡張構造 (簡略化)
ExtensionType pre_shared_key = 0x0029
struct {
opaque identity<1..2^16-1>;
uint32 obfuscated_ticket_age;
} PskIdentity;
struct {
opaque binder<32..255>; /* Hash of ClientHello up to the binders field */
} PskBinderEntry;
struct {
PskIdentity identities<7..2^16-1>;
PskBinderEntry binders<32..2^16-1>;
} PreSharedKeyExtension;
legacy_record_version は、後方互換性のためにTLS 1.2の値 (0x0303) を設定することが一般的ですが、TLS 1.3ではこのフィールドは無視され、SupportedVersions拡張が実際のバージョンを示します。
KeyShareEntryは、クライアントがサポートするDHEグループとその公開鍵を含みます。
PskIdentityは、クライアントが再利用したいPSKの識別子と、チケット発行からの経過時間 (obfuscated_ticket_age) を含みます。PskBinderEntryは、ClientHello全体に対するHMACであり、選択されたPSKの正当性を証明します。
相互運用性
TLS 1.2 との比較
| 特徴 |
TLS 1.2 |
TLS 1.3 (RFC 8446) |
| フルハンドシェイクRTT |
2 RTT |
1 RTT |
| セッション再開RTT |
1 RTT |
0 RTT (PSK-DHEまたはPSK) |
| 前方秘匿性 (PFS) |
オプション (DHE/ECDHE選択時のみ) |
必須 (DHE/ECDHEのみ) |
| 鍵交換モード |
RSA、DHE/ECDHE、PSKなど複数 |
DHE/ECDHE、PSK、PSK-DHEのみ |
| 暗号スイート |
多くのレガシー暗号 (RC4, CBCモード, MD5, SHA-1ベースのHMACなど) を含む |
現代的で強力な認証付き暗号 (AEAD) のみ (AES-GCM, ChaCha20-Poly1305) |
| プロトコルメッセージ |
ClientKeyExchangeなど、鍵交換用の追加メッセージあり |
ClientHello/ServerHello内の拡張で鍵交換情報をやり取りし、メッセージ数を削減 |
| ダウングレード攻撃対策 |
不十分 |
ServerHelloのランダム値に特定のパターンを導入し、検知機能を強化 |
HTTP/2 および HTTP/3 における TLS 1.3 の役割
HTTP/2 (TCP上):
HTTP/2はTCP上で動作するため、TLS 1.3が提供する1-RTTハンドシェイクは、接続確立のレイテンシを削減し、Webパフォーマンスを向上させます。しかし、TCP層で発生するHead-of-Line (HOL) Blockingの問題は、TLS 1.3を導入しても解決されません。複数のストリームがTCPセグメントを共有するため、1つのセグメントの紛失が全てのストリームの処理をブロックする可能性があります。
HTTP/3 (QUIC/UDP上):
HTTP/3は、TCPの代わりにUDP上で動作するQUICプロトコルを基盤としており、TLS 1.3を必須のセキュリティレイヤーとして利用します。QUICは、多重化ストリームをネイティブにサポートすることでTCPのHOL Blockingを回避します。さらに、TLS 1.3の0-RTTハンドシェイクと組み合わせることで、ほとんどの接続を0-RTTで確立でき、大幅なレイテンシ削減とユーザー体験の向上を実現します²。QUICのストリームレベルでの信頼性保証と輻輳制御は、TLS 1.3のセキュリティ層の上で機能します。
セキュリティ考慮
TLS 1.3はセキュリティを大幅に強化していますが、特定の機能には注意が必要です。
前方秘匿性 (Perfect Forward Secrecy, PFS):
TLS 1.3では、すべての鍵交換でDHE/ECDHEが必須とされたため、前方秘匿性が常に保証されます。これは、長期秘密鍵 (例えばサーバーのRSA証明書鍵) が将来漏洩しても、過去の通信内容が解読されることを防ぐ上で極めて重要です。
リプレイ攻撃 (0-RTT Early Data):
0-RTTデータは、ClientHelloと共に暗号化されて送信されるため、サーバーはクライアントの真正性を完全に検証する前にデータを受信します。これにより、攻撃者が0-RTTデータを傍受し、サーバーに繰り返し送信することで、同じ操作を複数回実行させる「リプレイ攻撃」のリスクが生じます。
RFC 8446のセクションE.5では、サーバーは0-RTTデータを処理する際に、冪等 (idempotent) な操作(何度実行しても結果が変わらない操作)に限定するか、強力なリプレイ検出メカニズムを実装するよう強く推奨されています¹。Google Security Blogも0-RTTの高速化を強調しつつ、このリプレイ攻撃への注意を促しています³。
ダウングレード攻撃対策:
TLS 1.3は、TLS 1.2やそれ以前のバージョンへの意図的なダウングレード攻撃を検知するメカニズムを導入しています。ServerHelloのrandomフィールドの下位8バイトを特定のパターン (0xCF) に設定し、legacy_session_id_echoが空でない場合にダウングレードが発生したことを示すことで、クライアントがこれを検知できます¹。
鍵更新 (Key Update):
TLS 1.3は、KeyUpdateメッセージを通じて、確立された接続のセッション鍵を定期的に更新する機能を提供します。これにより、長期にわたる通信セッションにおいても、鍵の漏洩リスクを低減し、前方秘匿性をさらに強化します。KeyUpdateメッセージは、次回の送信用および受信用トラフィック鍵を更新するように、相手に要求するために使用されます¹。
実装メモ
TLS 1.3の実装には、いくつかの重要な考慮事項があります。
MTU (Maximum Transmission Unit) / Path MTU:
TLSレコードレイヤーは、アプリケーションデータを複数のレコードに分割 (フラグメンテーション) することができます。しかし、ネットワークのPath MTUを超過するサイズのTLSレコードを送信すると、IPフラグメンテーションやパケットロスを引き起こし、パフォーマンスが低下する可能性があります。MTUに収まるようにTLSレコードサイズを調整し、可能な限りTCP MSS (Maximum Segment Size) やQUICのPacket sizeに適合させることが推奨されます。
HOL blocking 回避 (Head-of-Line Blocking):
TLS 1.3はTCP上で動作する場合、TCP自身のHOL blocking問題は解決しません。これは、TCPがバイトストリームの順序保証を行うため、1つのパケット損失が後続の全てのパケットの処理をブロックするためです。HTTP/3がQUICを使用することで、この問題をアプリケーションストリームレベルで解決しています。TLS 1.3をTCP上で利用する際は、この制約を理解しておく必要があります。
キュー制御と優先度:
特に0-RTTデータを利用する場合、そのデータの優先度を通常のアプリケーションデータとどのように扱うかが重要です。サーバー側で0-RTTデータを即座に処理するのか、それとも完全なハンドシェイクが完了するまで待機させるのか、アプリケーションの要件に応じて適切なキューイングと優先度設定が必要です。リプレイ攻撃のリスクを考慮し、0-RTTデータの処理には特に慎重な設計が求められます。
暗号学的乱数生成器 (CSPRNG):
鍵交換のセキュリティは、使用される乱数生成器の品質に直接依存します。TLS 1.3の実装では、エフェメラル鍵ペアの生成やその他の暗号操作に、強力なCSPRNGを確実に使用することが不可欠です。
まとめ
RFC 8446によって標準化されたTLS 1.3は、現代のインターネット通信におけるセキュリティとパフォーマンスの要求に応えるための重要な進化です。DHEおよびPSKを基盤とする鍵交換モード、1-RTTフルハンドシェイク、そして0-RTTハンドシェイクによる高速化は、Web体験を大きく改善します。同時に、常時前方秘匿性の強制、脆弱な暗号の排除、ダウングレード攻撃対策などにより、セキュリティも強化されました。
しかし、0-RTTの利便性と引き換えに生じるリプレイ攻撃のリスクは、実装者が十分に理解し、適切に対処すべき重要なセキュリティ考慮事項です。プロトコルエンジニアとして、これらの詳細を理解し、堅牢で安全なTLS 1.3の実装を設計することが求められます。
参照:
IETF. “RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3”. 2018年8月10日. https://datatracker.ietf.org/doc/html/rfc8446
Cloudflare Blog. “A detailed look at TLS 1.3”. 2018年11月9日. https://blog.cloudflare.com/tls-1-3-overview/
Google Security Blog. “TLS 1.3 everywhere”. 2018年8月22日. https://security.googleblog.com/2018/08/tls-13-everywhere.html
コメント