RFC 8446: TLS 1.3ハンドシェイク詳解

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

RFC 8446: TLS 1.3ハンドシェイク詳解

はじめに

インターネット通信のセキュリティを担保する基盤技術であるTLS(Transport Layer Security)プロトコルは、常に進化を続けています。その最新安定版であるTLS 1.3は、RFC 8446 [1] として2018年8月に標準化されました。本記事では、このRFC 8446で定義されるTLS 1.3のハンドシェイクプロセスに焦点を当て、その詳細、主要な変更点、セキュリティ考慮事項、および実装上の注意点を解説します。

背景

TLSは、クライアントとサーバー間の安全な通信チャネルを確立するために設計されました。これまでのバージョン、特にTLS 1.2は長らく広く利用されてきましたが、時間の経過とともに明らかになったセキュリティ上の脆弱性や、Webアプリケーションのパフォーマンス要求の高まりに対応するため、抜本的な改訂が求められていました。特に、ハンドシェイクにかかる時間や、レガシーな暗号スイートに起因する攻撃リスクが課題でした。

設計目標

TLS 1.3は、以下の主要な設計目標を掲げて開発されました。

  1. セキュリティの強化: 既知の脆弱性を持つ暗号アルゴリズムや機能(例: RSA鍵交換、SHA-1、CBCモード暗号、MD5など)を排除し、Perfect Forward Secrecy (PFS) を常に提供する鍵交換方式(ECDHE/DHE)のみをサポートします。また、ハンドシェイクの大部分を暗号化することで、中間者攻撃に対する耐性を向上させました。

  2. パフォーマンスの向上: ハンドシェイクのラウンドトリップタイム(RTT)をTLS 1.2の2-RTTから1-RTTに削減し、さらに0-RTT (Zero Round Trip Time) モードを導入することで、初回接続以降のデータ転送開始を高速化しました。

  3. 簡素化: 暗号スイートの選択肢を大幅に削減し、複雑な設定ミスや実装上のバグの可能性を低減しました。

  4. プライバシーの向上: ハンドシェイクメッセージの一部を暗号化することで、第三者による通信内容の傍受を困難にしました。

TLS 1.3ハンドシェイクの概要

RFC 8446 [1] は、TLS 1.3プロトコルの全ての仕様を定義しています。TLS 1.3のハンドシェイクは、TLS 1.2以前と比較して大幅に簡素化・高速化されています。

主要な変更点とRFC 8446

TLS 1.3における主な変更点は以下の通りです [2][3]。

  • ハンドシェイクの高速化: 1-RTTおよび0-RTTの導入。

  • 鍵交換方式の限定: ECDHE (Ephemeral Elliptic Curve Diffie-Hellman) または DHE (Ephemeral Diffie-Hellman) のみをサポートし、PFSを保証。RSA鍵交換は廃止。

  • 暗号スイートの簡素化: 認証付き暗号 (AEAD: Authenticated Encryption with Associated Data) のみを採用。TLS 1.2では多くの暗号スイートが利用可能でしたが、TLS 1.3では5つの主要なAEADアルゴリズムに集約されました。

  • ハンドシェイクメッセージの暗号化: ServerHello以降のハンドシェイクメッセージ(EncryptedExtensions, Certificate, CertificateVerifyなど)が鍵交換によって確立された共有秘密鍵で暗号化されます。

  • セッション再開の変更: セッションチケットがPre-Shared Key (PSK) となり、0-RTTデータ送信の基盤となります。

  • ダウングレード攻撃対策: 特殊なランダムバイトの挿入や、Finishedメッセージの内容検証により、古いTLSバージョンへの意図的なダウングレードを防ぎます。

ヘッダ/フレーム構造(ClientHello例)

TLS 1.3のメッセージは、Record Layerによってカプセル化されます。ハンドシェイクメッセージは Handshake レコードタイプを使用します。

以下は、ClientHelloメッセージの主要な構造を簡略化したものです。

struct {
    ProtocolVersion legacy_version = 0x0303; // TLS 1.2
    Random random; // 32 bytes
    opaque legacy_session_id<0..32>; // 通常は空
    CipherSuite cipher_suites<2..2^16-2>; // クライアントがサポートする暗号スイートのリスト
    Extension extensions<0..2^16-1>; // 鍵共有、SNI、PSK、SupportedVersionsなど
} ClientHello;
  • legacy_version: TLS 1.3では常に 0x0303 (TLS 1.2) を含み、バージョンネゴシエーションは SupportedVersions 拡張で行われます。これはダウングレード攻撃対策の一環です。

  • random: クライアントが生成する32バイトの乱数。鍵生成の材料となります。

  • cipher_suites: クライアントがサポートする暗号スイートのリスト。TLS 1.3ではAEAD方式のみが提示されます。

  • extensions: TLS 1.3の多くの機能は拡張として提供されます。重要なものとして、key_share (鍵共有情報)、supported_versions (サポートするTLSバージョン)、server_name (SNI)、pre_shared_key (PSKによる0-RTT/セッション再開用) などがあります。

ハンドシェイク詳解

TLS 1.3のハンドシェイクは、高速性とセキュリティを両立させるために設計されています。

1-RTTハンドシェイクフロー

TLS 1.3の標準的なハンドシェイクは、鍵交換が完了するまでにクライアントとサーバー間で1回のラウンドトリップで済みます [1][2]。

sequenceDiagram
    participant Client
    participant Server

    Client ->> Server: ClientHello (Supported Versions, Key Share, Cipher Suites, SNI)
    Note over Server: Server processes ClientHello, selects parameters.
    Server ->> Client: ServerHello (Selected Version, Selected Key Share, Selected Cipher Suite)
    Server ->> Client: EncryptedExtensions (ALPN, max_fragment_length, etc.)
    Server ->> Client: Certificate (Server's certificate chain)
    Server ->> Client: CertificateVerify (Proof of certificate ownership, signed with server's private key)
    Server ->> Client: Finished (Handshake integrity check)
    Note over Client: Client verifies server messages, generates master secret.
    Client ->> Server: Finished (Handshake integrity check)
    Note over Client: Handshake complete.
    Client ->> Server: Application Data (Encrypted)
    Server ->> Client: Application Data (Encrypted)
  1. ClientHello: クライアントはサポートするTLSバージョン、暗号スイート、鍵共有方式 (KeyShare) を含む ClientHello メッセージを送信します。TLS 1.3では、クライアントは複数の鍵共有方式(例:x25519, secp256r1など)の公開鍵を key_share 拡張で事前に提供できます。

  2. ServerHello: サーバーは ClientHello を受け取り、そこから最適なTLSバージョン(TLS 1.3)、暗号スイート、クライアントが提示した鍵共有の中から一つを選択し、その公開鍵を key_share 拡張に含めて ServerHello を返します。この時点で、クライアントとサーバーは共通の鍵共有秘密を計算できるようになります。

  3. EncryptedExtensions: サーバーは、ServerHello の直後に、ネゴシエートされた鍵で暗号化された EncryptedExtensions メッセージを送信します。これには、ALPN (Application-Layer Protocol Negotiation) や最大フラグメント長など、以前は暗号化されていなかった拡張情報が含まれます。

  4. Certificate / CertificateVerify: サーバーは自身の証明書チェーン (Certificate) と、その証明書の所有を証明する署名 (CertificateVerify) を送信します。これらも暗号化されます。

  5. Finished: サーバーは、ハンドシェイクの整合性を保証するハッシュ (Finished) を送信します。これは、鍵交換以降の全てのハンドシェイクメッセージのトランスクリプトに対するMAC (Message Authentication Code) です。

  6. Client Finished: クライアントはサーバーからのメッセージを検証し、自身の Finished メッセージを送信します。これにより、ハンドシェイクが完了し、アプリケーションデータの交換が開始されます。

0-RTTハンドシェイクフローとEarly Data

TLS 1.3は、以前に接続したことのあるクライアントに対して、0-RTT (Zero Round Trip Time) モードを提供し、初回データ送信のレイテンシを削減します [1][4]。

sequenceDiagram
    participant Client
    participant Server

    Note over Client,Server: Previous successful TLS 1.3 handshake occurred.
    Note over Server: Server issued NewSessionTicket (with PSK and Early Data key).

    Client ->> Server: ClientHello (includes PSK Identity and Early Data Key Share)
    Client ->> Server: Early Data (Application Data, encrypted with Early Data Key)
    Note over Server: Server decrypts and processes Early Data.
    Note over Server: Server processes ClientHello, selects parameters.
    Server ->> Client: ServerHello (Selected Version, Selected Key Share, Selected Cipher Suite)
    Server ->> Client: EncryptedExtensions (ALPN, max_fragment_length, etc.)
    Server ->> Client: Certificate (Server's certificate chain)
    Server ->> Client: CertificateVerify (Proof of certificate ownership, signed with server's private key)
    Server ->> Client: Finished (Handshake integrity check)
    Note over Client: Client verifies server messages, generates master secret.
    Client ->> Server: Finished (Handshake integrity check)
    Note over Server: Handshake complete.
    Server ->> Client: Application Data (Encrypted)

0-RTTハンドシェイクは、以前のセッションから取得したPSK (Pre-Shared Key) を使用します。クライアントは、ClientHello メッセージに pre_shared_key 拡張を含め、同時に Early Data と呼ばれるアプリケーションデータを暗号化して送信できます。サーバーは、PSKを検証し、Early Data を復号して処理を開始できます。このメカニズムにより、1 RTT分の時間を節約し、Webページのロード時間などを短縮できます。

ただし、0-RTTにはリプレイ攻撃のリスクがあります。これは、悪意のある攻撃者が Early Data を傍受し、それを再送信することで、サーバーに同じ操作を複数回実行させる可能性があるためです。このリスクを軽減するため、サーバーは Early Data に使用する鍵を一回限りとする仕組みを導入したり、アプリケーション層で冪等な操作のみを許可したりするなどの対策が必要です。

既存プロトコルとの比較

TLS 1.3は、以前のTLSバージョンや、それを基盤とする他のプロトコルと比較して、いくつかの顕著な改善点があります。

TLS 1.2との比較

TLS 1.3は、TLS 1.2と比較して、セキュリティとパフォーマンスの両面で大幅な進歩を遂げています。

  • ハンドシェイクの複雑さ:

    • TLS 1.2: 鍵交換に2回のRTTが必要でした。また、鍵交換方式の選択肢が多く、複雑性が高かった。

    • TLS 1.3: 1回のRTTで鍵交換が完了し、0-RTTオプションも提供。鍵交換方式はPFSを保証する方式に限定され、簡素化された。

  • 暗号スイート:

    • TLS 1.2: RSA鍵交換、CBCモード暗号、RC4、MD5/SHA-1ハッシュなど、多くのレガシーで脆弱な暗号アルゴリズムをサポートしていた。

    • TLS 1.3: 認証付き暗号 (AEAD) のみをサポートし、AES-GCMやChaCha20-Poly1305など、少数の強力な暗号スイートに限定。脆弱なアルゴリズムはすべて廃止された。

  • ハンドシェイクの可視性:

    • TLS 1.2: クライアント証明書や拡張情報など、ハンドシェイクの多くの部分が暗号化されずに転送されていた。

    • TLS 1.3: ServerHello以降のほとんどのハンドシェイクメッセージが暗号化され、プライバシーが向上した。

  • セッション再開:

    • TLS 1.2: セッションIDやセッションチケットを使用したが、0-RTTは不可能。

    • TLS 1.3: PSK (Pre-Shared Key) を使用し、0-RTTデータ送信をサポート。

HTTP/2 (TCP+TLS 1.3) と HTTP/3 (QUIC+TLS 1.3)

TLS 1.3は、HTTP/2およびHTTP/3の基盤となるセキュリティプロトコルです。

  • HTTP/2 (TCP + TLS 1.3):

    • HTTP/2はTCP上で動作するため、依然としてTCPのヘッドオブラインブロッキング (HOL blocking) の影響を受けます。つまり、一つのTCPストリームでパケットロスが発生すると、その回復が完了するまで他の全てのストリームの処理が停止する可能性があります。

    • TLS 1.3の1-RTTハンドシェイクにより、TLSレイヤーでのレイテンシは削減されますが、TCPハンドシェイク(3-way handshake)と合わせると、データ送信開始までに合計2-RTTが必要となります。

  • HTTP/3 (QUIC + TLS 1.3):

    • HTTP/3はQUICプロトコル上で動作し、QUICはUDPをベースとしています。QUICは、独自の信頼性保証、フロー制御、輻輳制御機構を持つため、TCPのHOL blockingの問題を解決します。複数のストリームを独立して多重化できるため、一つのストリームでのパケットロスが他のストリームに影響を与えません。

    • QUICはTLS 1.3をそのハンドシェイクと暗号化の基盤として組み込んでいるため、QUIC自身のハンドシェイクとTLS 1.3のハンドシェイクが統合され、初回接続から1-RTTでアプリケーションデータの送信を開始できます。以前のセッション情報があれば、0-RTTも可能です。これにより、HTTP/2と比較してさらに高速な接続確立が実現されます。

セキュリティ考慮事項

TLS 1.3は、以前のバージョンよりもセキュリティが大幅に強化されていますが、いくつかの重要な考慮事項があります。

ダウングレード攻撃への対策

TLS 1.3は、意図しない古いTLSバージョンへのダウングレード攻撃に対して、複数の対策を講じています [1]。

  • ClientHelloのlegacy_versionフィールド: クライアントはClientHellolegacy_versionフィールドに0x0303 (TLS 1.2) を設定し、実際のサポートバージョンはsupported_versions拡張で伝えます。サーバーはlegacy_version0x0303以外の場合、TLS 1.3ハンドシェイクを拒否できます。

  • Finishedメッセージのrandomバイト: TLS 1.3サーバーは、ServerHellorandomフィールドの末尾8バイトに特殊な値 (0x44 0x4F 0x57 0x4E 0x47 0x52 0x44 0x01 または 0x44 0x4F 0x57 0x4E 0x47 0x52 0x44 0x00) を挿入します。これは、攻撃者がTLS 1.3のClientHelloをTLS 1.2のClientHelloに改ざんし、TLS 1.2ハンドシェイクを誘発した場合に、クライアントがこの不正なrandomバイトを発見し、ダウングレードを検知できるようにするためです。

リプレイ攻撃 (0-RTT)

0-RTTモードは、高速化の利点がある一方で、リプレイ攻撃のリスクを伴います [1]。攻撃者が Early Data を傍受し、それを再送信することで、サーバーに対して同じ処理を複数回実行することが可能です。

  • サーバー側の対策:

    • サーバーは NewSessionTicket で発行するPSKとそれに対応する Early Data 鍵を一回限りで使用するように設計するべきです。再利用を許すとリプレイ攻撃のリスクが高まります。

    • サーバーは、Early Data に関連するリソースを冪等性(何度実行しても結果が同じ)が保証された操作に限定すべきです。GETリクエストのような参照操作は適していますが、POSTリクエストのような状態変更を伴う操作は慎重に扱う必要があります。

    • サーバーは Age 拡張を使用して、Early Data が許容可能な時間範囲内にあるかを確認できます。

キー更新とPerfect Forward Secrecy (PFS)

TLS 1.3は、Forward Secrecyを標準で提供し、通信中にキー更新をサポートします [1]。

  • Perfect Forward Secrecy (PFS): TLS 1.3では、ECDHEまたはDHE鍵交換のみが必須とされているため、セッションごとに新しい一時的な秘密鍵が生成されます。これにより、長期的な秘密鍵(例:サーバーの秘密鍵)が漏洩しても、過去のセッションの暗号化されたデータが復号されることはありません。

  • キー更新: 長期間のセッションでは、暗号鍵が漏洩するリスクを低減するために、定期的な鍵更新が推奨されます。TLS 1.3は KeyUpdate メッセージを導入し、クライアントまたはサーバーが新しい鍵を要求し、それを使って以降のデータを暗号化できるようになります。

その他のセキュリティ改善

  • レガシー暗号の廃止: MD5、SHA-1、RSA鍵交換、DES、3DES、RC4、CBCモード暗号など、既知の脆弱性を持つすべてのレガシー暗号アルゴリズムがTLS 1.3から削除されました。

  • Handshakeの大部分の暗号化: ServerHello以降のハンドシェイクメッセージが暗号化されるため、ネットワーク上の第三者によるプロトコル情報の傍受や、アクティブな攻撃が困難になりました。

実装上の注意点

TLS 1.3の実装には、パフォーマンスと安定性を最大化するためにいくつかの注意点があります。

MTUとPath MTU Discovery

  • Early Dataサイズ: 0-RTTの Early Data は、最初の ClientHello パケットに含まれて送信されます。このパケットは、ネットワークのMTU (Maximum Transmission Unit) を超えないように注意深くサイズを調整する必要があります。特に、Early Data はTCPフラグメンテーションの対象とならない可能性があるため、Path MTU Discovery (PMTUD) が適切に機能しない環境では問題となることがあります。

  • PMTUD: PMTUDは、通信経路上の最小MTUを動的に発見するためのメカニズムですが、ファイアウォールによってICMPメッセージがブロックされることがあり、PMTUDがうまく機能しない場合があります。これにより、パケットがドロップされる可能性が生じます。実装者は、MTUを考慮した Early Data の最大サイズ設定や、PMTUDの動作を検証する必要があります。

HOL Blockingの回避

  • TCPのHOL Blocking: TLS 1.3自体はハンドシェイクを高速化しますが、TCPの上で動作する場合、TCPのヘッドオブラインブロッキング (HOL blocking) の影響は残ります。一つのTCPセグメントが失われると、その回復が完了するまで、同じTCPコネクション上の後続データがすべてブロックされます。

  • QUIC/HTTP/3での回避: QUIC (TLS 1.3を使用) は、多重化されたストリームをUDP上で独立して処理することで、TCPのHOL blockingを根本的に回避します。レイテンシに敏感なアプリケーションでは、HTTP/3とQUICの採用を検討するべきです。

キュー制御と優先度付け

  • アプリケーションデータ: TLS層で暗号化されたアプリケーションデータをネットワークに送信する際、ネットワークスタックのキューイングとスケジューリングの挙動が、エンドツーエンドのレイテンシに影響を与えます。

  • 優先度: 複数のストリームが存在する場合(HTTP/2やQUICなど)、重要度の高いリソース(例:HTML、CSS)を優先して送信し、重要度の低いリソース(例:画像、動画)の送信を遅らせるようなキュー制御や優先度付けを適切に行うことが、ユーザーエクスペリエンス向上に繋がります。これはTLS層ではなく、その上のアプリケーション層やトランスポート層で管理されることが多いです。

乱数生成とサイドチャネル攻撃

  • 暗号論的乱数: TLS 1.3の鍵生成には、高品質で予測不可能な暗号論的乱数ジェネレーター (CSPRNG) が必須です。不適切な乱数生成は、鍵推測攻撃につながる可能性があります。

  • サイドチャネル攻撃: TLSの実装は、タイミング攻撃やキャッシュ攻撃などのサイドチャネル攻撃に対して脆弱である可能性があります。例えば、MACの検証時間や、復号処理の時間差が攻撃者に情報を提供する可能性があります。堅牢な実装では、これらの攻撃に対して一定の対策を講じる必要があります(例: 定数時間アルゴリズムの使用)。

まとめ

RFC 8446で標準化されたTLS 1.3は、Webのセキュリティとパフォーマンスを大幅に向上させる重要なプロトコルです。ハンドシェイクの簡素化と高速化(1-RTT、0-RTT)、レガシー暗号の排除、Perfect Forward Secrecyの常時提供、そしてダウングレード攻撃への対策は、現代のインターネットに求められるセキュリティ要件を満たすものです。

実装においては、0-RTTのリプレイ攻撃リスクへの対応、MTUを考慮したEarly Dataのサイズ設計、そして基盤となるトランスポートプロトコル(TCPまたはQUIC)との相互作用を理解することが重要です。特にHTTP/3とQUICにおけるTLS 1.3の採用は、TCPのHOL blocking問題を解消し、さらなるパフォーマンス向上をもたらします。ネットワークエンジニアとして、TLS 1.3のこれらの特性を深く理解し、適切に活用することで、より安全で高速な通信環境の構築に貢献できるでしょう。


参考文献:

[1] D. Rescorla, “The Transport Layer Security (TLS) Protocol Version 1.3”, RFC 8446, August 2018. https://datatracker.ietf.org/doc/html/rfc8446 (2018年8月公開) [2] Cloudflare Blog. “A Thorough Guide to TLS 1.3”. https://blog.cloudflare.com/a-thorough-guide-to-tls-1-3 (2024年3月1日公開) [3] Mozilla Security Blog. “TLS 1.3 is here!”. https://security.mozilla.org/blog/2018/08/13/tls-1-3-is-here/ (2018年8月13日公開) [4] Google Security Blog. “Bringing a faster, more secure internet to everyone with TLS 1.3”. https://security.googleblog.com/2018/10/bringing-faster-more-secure-internet-to.html (2018年10月24日公開)

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました