TLS 1.3ハンドシェイク(RFC 8446)の詳細と実装上の考慮点

Tech

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

TLS 1.3ハンドシェイク(RFC 8446)の詳細と実装上の考慮点

背景

Transport Layer Security (TLS) は、インターネット上で安全な通信を確立するためのプロトコルであり、Webブラウジング、電子メール、音声/ビデオ通話など、多岐にわたるサービスで利用されています。従来のバージョンであるTLS 1.2には、いくつかの課題が存在しました。例えば、ハンドシェイクの複雑性、脆弱な暗号スイートの利用可能性、そして完全なセッション確立までに必要なネットワークのラウンドトリップ時間(RTT)が複数回発生することによる遅延です。これらの課題に対処するため、IETF(Internet Engineering Task Force)は2018年8月にRFC 8446としてTLS 1.3を標準化しました[1]。

設計目標

TLS 1.3は、以下の主要な設計目標を掲げています。

  • パフォーマンスの向上: ハンドシェイクに必要なRTT数を削減し、特にセッション再開時のオーバーヘッドを最小限に抑えます。これにより、Webページの読み込み速度向上などに寄与します。

  • セキュリティの強化: 過去のTLSバージョンで発見された脆弱性のある機能や暗号スイート(例: RC4, SHA-1, MD5, 静的RSA鍵交換、静的Diffie-Hellman)をプロトコルから完全に削除し、デフォルトでPerfect Forward Secrecy (PFS) を強制します。

  • プロトコルの簡素化: ハンドシェイクメッセージフローを簡略化し、実装の複雑さを軽減するとともに、プロトコルの理解と解析を容易にします。

  • プライバシーの強化: ハンドシェイクの一部を暗号化することで、中間者による情報収集を困難にします。

詳細

TLS 1.3の標準ハンドシェイク

TLS 1.3のハンドシェイクは、TLS 1.2と比較して大幅に簡素化され、多くの場合1-RTTで完了します。鍵交換はすべてDiffie-Hellman (DH) または楕円曲線Diffie-Hellman (ECDH) に基づいており、Perfect Forward Secrecyが保証されます。

TLS 1.3の標準的なハンドシェイクフローを以下に示します。

sequenceDiagram
    autonumber
    participant Client as C
    participant Server as S

    C ->> S: ClientHello |(supported_versions, key_share, cipher_suites, extensions)|
    S ->> C: ServerHello |(selected_version, selected_cipher_suite, key_share)|
    Note over S,C: 共有鍵確立
    S ->> C: ChangeCipherSpec |(optional, for middleboxes)|
    S ->> C: EncryptedExtensions |(ALPN, max_fragment_length, etc.)|
    S ->> C: Certificate |(If server authentication required)|
    S ->> C: CertificateVerify |(Signature over handshake context)|
    S ->> C: Finished |(HMAC over handshake transcript)|
    C ->> S: ChangeCipherSpec |(optional, for middleboxes)|
    C ->> S: Finished |(HMAC over handshake transcript)|
    C -->> S: Application Data |(encrypted)|
    S -->> C: Application Data |(encrypted)|
  1. ClientHello: クライアントは、サポートするTLSバージョン、暗号スイート、鍵共有方式(key_share)、およびその他の拡張情報をサーバーに送信します。key_share拡張により、クライアントは複数のDH/ECDH鍵を推測で提供し、サーバーはそれらのうち1つを選択できます。

  2. ServerHello: サーバーは、クライアントが提示した情報から、選択したTLSバージョン(必ずTLS 1.3)、選択した暗号スイート、および自身のkey_shareを返します。この時点で、クライアントとサーバーは共通の鍵共有材料を確立し、セッション鍵を導出できます。

  3. EncryptedExtensions: 鍵確立後、サーバーはTLS 1.2では暗号化されていなかった追加の拡張情報を暗号化して送信します。

  4. Certificate / CertificateVerify: サーバー認証が必要な場合、サーバーは自身の証明書と、ハンドシェイクメッセージのトランスクリプトに対するデジタル署名(CertificateVerify)を送信します。

  5. Finished: サーバーは、ハンドシェイクトランスクリプト全体を認証するFinishedメッセージを送信します。

  6. Finished (Client): クライアントも同様に、自身のFinishedメッセージを送信します。

  7. Application Data: 以降、アプリケーションデータは確立されたセッション鍵で暗号化されて送受信されます。

ChangeCipherSpecメッセージは、TLS 1.3の鍵確立には直接関与せず、TLS 1.2のハンドシェイクを想定しているレガシーな中間ボックス(ミドルボックス)との互換性のために、必要に応じて送信されます。

0-RTTハンドシェイク(Early Data)

TLS 1.3の最も画期的な機能の一つが0-RTTハンドシェイクです。これは、以前に確立されたセッションの情報を再利用することで、ClientHelloと共にアプリケーションデータを送信可能にし、最初のデータ転送までのRTTを0に削減します。これはHTTP/2やHTTP/3 (QUIC) のようなプロトコルにおいて、特に初回リクエストのレイテンシ削減に大きく貢献します。

0-RTTハンドシェイクのフローを以下に示します。

sequenceDiagram
    autonumber
    participant Client as C
    participant Server as S

    Note over C,S: 事前共有鍵 (PSK) の確立済み
    C ->> S: ClientHello |(PSK_key_exchange_modes, pre_shared_key, early_data)|
    C ->> S: Application Data |(early data, encrypted with PSK)|
    S ->> C: ServerHello |(selected_version, selected_cipher_suite, key_share, pre_shared_key)|
    S ->> C: ChangeCipherSpec |(optional)|
    S ->> C: EncryptedExtensions |(...)|
    S ->> C: Certificate |(optional)|
    S ->> C: CertificateVerify |(optional)|
    S ->> C: Finished |(HMAC over handshake transcript)|
    S ->> C: NewSessionTicket |(If server provides new PSK for future 0-RTT)|
    C ->> S: ChangeCipherSpec |(optional)|
    C ->> S: Finished |(HMAC over handshake transcript)|
    C -->> S: Application Data |(encrypted)|
    S -->> C: Application Data |(encrypted)|

0-RTTハンドシェイクは、以前のセッションでサーバーから発行された「NewSessionTicket」と、それに関連付けられた事前共有鍵(PSK)を利用します。クライアントは、ClientHellopre_shared_key拡張を含め、同時にアプリケーションデータをearly_dataとして送信します。サーバーはこれを受け取り、PSKを検証できれば、即座にアプリケーションデータを処理し、応答を返すことが可能です。ただし、0-RTTデータはリプレイ攻撃のリスクを伴うため、注意が必要です。

TLSレコード層の構造

TLS 1.3では、レコード層の構造も簡素化されました。TLS 1.2で存在したTLSCompressed層は削除され、TLSPlaintextTLSCiphertextの2つの層が基本となります。

TLSPlaintext Record Header: レコード層のヘッダは、すべてのTLSバージョンで共通の基本的な構造を持ちます。

TLSPlaintext Record Header:
  type: 8 bits (Content Type, e.g., handshake(22), application_data(23))
  legacy_record_version: 16 bits (常に 0x0303 for TLS 1.3)
  length: 16 bits (ペイロードの長さ, 最大 16384 bytes)

TLSCiphertext Record (Encrypted): TLS 1.3では、すべてのハンドシェイクおよびアプリケーションデータは鍵確立後に暗号化されます。認証付き暗号 (AEAD) が必須となり、ペイロードにはオリジナルのコンテンツタイプも含まれるようになります。

TLSCiphertext Record (encrypted):
  OpaqueType: 8 bits (オリジナル Content Type, 暗号化された形でペイロードの一部)
  Authenticated Encryption with Associated Data (AEAD) Payload:
    Nonce: (implicit なシーケンス番号などから導出される)
    EncryptedContent: (アプリケーションデータ、ハンドシェイク、アラートなど)
    Padding: 0-255 bytes (オプション、全てゼロ)
    ContentType: 8 bits (実際の Content Type, 暗号化された形でペイロードの一部)
  Authentication Tag: N bits (AEADアルゴリズムに依存, 例: AES-GCMなら 128 bits)

OpaqueTypeフィールドは、ミドルボックスがTLS 1.2以前のレコード層ヘッダを解析しようとしたときに、常にapplication_dataと認識させるための互換性措置です。実際のコンテンツタイプはContentTypeとして暗号化されたペイロード内に含まれます。

既存プロトコルとの比較

TLS 1.3は、TLS 1.2と比較して以下の点で大きな違いがあります。

  • ハンドシェイクのRTT数:

    • TLS 1.2: フルハンドシェイクで2-RTT以上、セッション再開で1-RTT。

    • TLS 1.3: フルハンドシェイクで1-RTT、セッション再開で0-RTTまたは1-RTT。

  • 鍵交換:

    • TLS 1.2: RSA、DH、ECDHなど多様な鍵交換をサポート。静的鍵交換も可能。

    • TLS 1.3: すべての鍵交換でDH/ECDHを必須とし、Perfect Forward Secrecyを強制。

  • 暗号スイート:

    • TLS 1.2: 多くの古い・脆弱な暗号スイートをサポートし、設定ミスで安全でない組み合わせが使われるリスクがあった。

    • TLS 1.3: 安全性が確認された少数のAEADベースの暗号スイートのみをサポートし、設定を簡素化。

  • セッション再開:

    • TLS 1.2: セッションIDまたはセッションチケットを使用。

    • TLS 1.3: NewSessionTicketとPSK(Pre-Shared Key)のみを使用し、0-RTTを可能にする。

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

    • TLS 1.2: 特定のダウングレード試行に対して脆弱性があった。

    • TLS 1.3: ClientHelloServerHelloの予約バイトの一部を利用して、明示的なダウングレード保護メカニズムを導入。

セキュリティ考慮

TLS 1.3はセキュリティ強化が最優先事項とされていますが、それでも実装者や運用者が注意すべき点がいくつかあります。

  • リプレイ攻撃 (0-RTT):

    • 0-RTTデータは、クライアントがClientHelloと共に送信する早期データであり、セッション再開用のPSKで暗号化されます。このPSKは、サーバーが以前に発行したセッションチケットから導出されます。

    • 攻撃者がこの早期データを傍受し、サーバーに再送した場合、サーバーがそれを正当なリクエストとして処理してしまう可能性があります。これがリプレイ攻撃です。

    • 対策: RFC 8446では、サーバーはリプレイ攻撃を防ぐために、受信した早期データの正当性を厳密に検証する必要があります[1]。具体的には、サーバーはNewSessionTicketを発行する際に、リプレイ保護メカニズム(例: ワンタイムナンス、時刻ベースの有効期限、チケットの消費管理など)を組み込む必要があります。金融取引など、リプレイが重大な結果を招くような場合は、0-RTTの使用を避けるか、追加のアプリケーション層でのリプレイ対策を講じるべきです。

  • ダウングレード攻撃:

    • 攻撃者がクライアントとサーバー間の通信を改ざんし、より古い(そして脆弱な)TLSバージョンでの接続を強制しようとすることがダウングレード攻撃です。

    • 対策: TLS 1.3は、ClientHelloおよびServerHellolegacy_versionフィールドとrandomフィールド内の特定のバイトシーケンスを用いて、この種の攻撃を検出します[1]。これにより、TLS 1.3対応のクライアントやサーバーが、意図せず古いプロトコルにダウングレードされることを防ぎます。

  • キー更新:

    • 長期にわたるセッションでは、同じセッション鍵を使い続けることで、鍵漏洩のリスクが高まります。

    • 対策: TLS 1.3は、ハンドシェイク後にも鍵を更新できるKeyUpdateメッセージを提供します。クライアントまたはサーバーは、このメッセージを送信することで、新しいセッション鍵を導出し、通信の機密性を維持できます。定期的な鍵更新は、万一鍵が漏洩しても、過去の通信や将来の通信への影響を局所化するために重要です。

  • Perfect Forward Secrecy (PFS) の強制:

    • PFSは、あるセッション鍵が侵害されても、過去のセッション鍵や将来のセッション鍵には影響が及ばないことを保証する特性です。

    • 対策: TLS 1.3では、すべての主要な鍵交換(DH/ECDH)が一時的な(エフェメラルな)鍵ペアを使用するため、PFSがデフォルトで提供されます。これにより、サーバーの長期秘密鍵が漏洩した場合でも、過去の通信の復号化は不可能になります。

実装メモ

TLS 1.3の実装には、プロトコル仕様の理解に加え、ネットワークエンジニアリングの観点からの考慮点があります。

  • MTU (Maximum Transmission Unit) / Path MTU:

    • ClientHelloおよびServerHelloメッセージは、サポートされる拡張や証明書のサイズによって肥大化する可能性があります。特に、複数のkey_shareを送信する場合や、複数のALPNプロトコル名を提示する場合、メッセージサイズが増加します。

    • Certificateメッセージは、サーバ証明書チェーンが長い場合に非常に大きくなることがあります。

    • これらのメッセージがネットワークのMTUを超える場合、IPフラグメンテーションが発生し、パケットロスやパフォーマンス低下の原因となる可能性があります。Path MTU Discovery (PMTUD) を活用するか、メッセージサイズを適切に管理することが重要です。

  • HOL (Head-of-Line) Blocking回避:

    • TCP上でのTLS 1.3は、TCP自体の特性上、HOLブロッキングの影響を受けます。特に、一つのTCPストリーム上で複数のアプリケーションデータが多重化されている場合、前のパケットが失われると後続のすべてのデータが処理を待たされます。

    • 対策: HTTP/3 (QUIC) は、UDP上で独自の信頼性・順序保証メカニズムを持ち、TLS 1.3をそのトランスポート層として利用することで、このHOLブロッキングを大幅に軽減します。TCPベースの実装では、アプリケーション層での多重化を注意深く設計する必要があります。

  • キュー制御と優先度:

    • ハンドシェイクメッセージ(特にClientHelloServerHello)は、接続確立において非常に重要であり、他のアプリケーションデータよりも高い優先度で処理されるべきです。

    • 0-RTTで送信される早期アプリケーションデータは、通常のデータよりも優先度が高いことが一般的ですが、リプレイリスクを考慮した上での処理が必要です。

    • 実装では、これらのメッセージタイプに応じた送信キューの優先度付けや、受信時の処理順序を考慮した設計が求められます。

  • 乱数生成:

    • TLS 1.3では、セッション鍵の導出に強力な乱数が必要とされます。ClientHello.randomServerHello.randomは、鍵導出の重要な要素であり、暗号論的に強力な乱数ジェネレータを使用することが必須です。

    • 不適切な乱数生成は、鍵推測を容易にし、全体のセキュリティを著しく損ないます。

  • タイムスタンプの同期:

    • 0-RTTのリプレイ対策として、サーバーが発行するNewSessionTicketには有効期限が設定されることが一般的です。この有効期限はサーバーの時刻に依存するため、クライアントとサーバー間の時刻同期が重要になります。

    • NTP (Network Time Protocol) などを用いて、システム時刻が正確に同期されていることを確認すべきです。

まとめ

TLS 1.3(RFC 8446)は、Webのパフォーマンスとセキュリティを大幅に向上させる重要なプロトコルアップデートです。1-RTTハンドシェイクと0-RTTデータにより通信の遅延が削減され、古く脆弱な暗号スイートの排除とPerfect Forward Secrecyの強制によりセキュリティが強化されました。

ネットワークエンジニアとしては、このプロトコルの詳細な動作、特に0-RTTのリプレイ攻撃対策や、MTU・HOLブロッキングなどの実装上の考慮点を深く理解することが不可欠です。適切な実装と運用によって、TLS 1.3は安全で高速なインターネット通信の基盤をさらに強固なものとします。


参考文献

[1] E. Rescorla. “The Transport Layer Security (TLS) Protocol Version 1.3”. RFC 8446. IETF. August 2018. https://www.rfc-editor.org/rfc/rfc8446 (参照日: 2024-07-31)

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

コメント

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