TLS 1.3ハンドシェイク詳細解説

Tech

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

TLS 1.3ハンドシェイク詳細解説

背景

TLS (Transport Layer Security) は、インターネット上の通信を暗号化し、データの機密性、完全性、および認証性を保証するプロトコルです。その最新バージョンである TLS 1.3 (RFC 8446) は、2018年8月にIETFによって公開され、以前のバージョンであるTLS 1.2と比較して、パフォーマンスとセキュリティの両面で大幅な改善が図られています。TLS 1.2は長らく広く利用されてきましたが、複雑な鍵交換プロセス、脆弱な暗号スイートの存在、およびパフォーマンス上のオーバーヘッドが課題となっていました。特に、ハンドシェイクの遅延は、Webページの読み込み時間やAPI通信の応答性に大きな影響を与えていました。

設計目標

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

  • 低遅延化: ハンドシェイクのラウンドトリップ数を削減し、特に接続確立時間を短縮することで、ユーザー体験を向上させる。

  • セキュリティ強化: 脆弱な暗号アルゴリズムやプロトコル機能を排除し、前方秘匿性 (Perfect Forward Secrecy; PFS) を強制することで、通信の安全性を高める。

  • 複雑性の削減: プロトコル設計を簡素化し、実装ミスや設定ミスによるセキュリティホール発生のリスクを低減する。

  • プライバシーの向上: ハンドシェイクメッセージの一部を暗号化することで、通信内容に関するメタデータの漏洩を防ぐ。

TLS 1.3ハンドシェイク詳細

TLS 1.3のハンドシェイクは、以前のバージョンと比較してメッセージ交換が大幅に簡素化され、ほとんどのケースで1-RTT (Round Trip Time) で完了します。これは、鍵交換をハンドシェイクの初期段階に統合し、クライアントとサーバーが同時に鍵生成に参加できるようにすることで実現されています。

主要フェーズ

  1. ClientHelloとServerHello: クライアントが利用可能な暗号スイート、鍵共有グループ、鍵共有値などの情報を提示し、サーバーがその中から選択して応答します。

  2. 鍵交換(ECDHE): TLS 1.3では、楕円曲線ディフィー・ヘルマン (ECDHE) など、常に一時的な鍵交換メカニズムが使用され、Perfect Forward Secrecy (PFS) が強制されます。これにより、長期的な秘密鍵が漏洩しても過去の通信が解読されることを防ぎます。

  3. 暗号化された拡張 (EncryptedExtensions): ALPN (Application-Layer Protocol Negotiation) などの拡張情報が、ハンドシェイクの早い段階で暗号化されて交換されます。これにより、ハンドシェイクのプライバシーが向上します。

  4. 認証と検証 (Certificate, CertificateVerify): サーバーは自身の証明書と、その証明書が正当であることを示す署名情報を提供します。クライアントはこの情報を用いてサーバーの身元を検証します。

  5. ハンドシェイク完了 (Finished): クライアントとサーバーは、これまでのハンドシェイクメッセージ全体のハッシュ値にMAC (Message Authentication Code) を付加したFinishedメッセージを交換し、ハンドシェイクが安全に完了したことを相互に確認します。

ハンドシェイクシーケンス(1-RTT)

通常の状態でのTLS 1.3ハンドシェイクは、以下のシーケンスで1-RTTで完了します。

sequenceDiagram
    participant C as クライアント
    participant S as サーバー
    C -> S: ClientHello (サポートTLSバージョン, 暗号スイート, 鍵共有グループ, 鍵共有値, 拡張)
    S -- >C: ServerHello (選択TLSバージョン, 暗号スイート, 鍵共有グループ, 鍵共有値)
    S -- >C: EncryptedExtensions (ALPN, その他の設定)
    S -- >C: Certificate (サーバー証明書, オプション)
    S -- >C: CertificateVerify (証明書の署名検証, オプション)
    S -- >C: Finished (サーバーハンドシェイクハッシュのMAC)
    activate C
    C ->> C: 共有秘密鍵導出 & ハンドシェイク完了
    deactivate C
    C -> S: Finished (クライアントハンドシェイクハッシュのMAC)
    activate S
    S ->> S: 共有秘密鍵導出 & ハンドシェイク完了
    deactivate S
    C -> S: [Application Data] (暗号化済み)
    S -> C: [Application Data] (暗号化済み)

鍵導出プロセス

TLS 1.3では、HKDF (HMAC-based Key Derivation Function) を用いて、複数の秘密鍵が段階的に導出されます。これにより、各フェーズで使用される鍵が分離され、セキュリティが向上します。

flowchart TD
    HSK_0["PSK(\"Pre-Shared Key\") または Zero-Length"] --|initial_secret|--> HSK_1["Handshake Secret"]
    HSK_1 --|derive_early_secret|--> HSK_ES["Early Secret"]
    HSK_ES --|derive_client_early_traffic_secret|--> CTS["Client Early Traffic Secret"]
    HSK_ES --|derive_binder_key|--> BK["Binder Key"]
    HSK_1 --|鍵交換共有秘密|--> HSK_HS["Handshake Secret (re-derived)"]
    HSK_HS --|derive_client_handshake_traffic_secret|--> CHTS["Client Handshake Traffic Secret"]
    HSK_HS --|derive_server_handshake_traffic_secret|--> SHTS["Server Handshake Traffic Secret"]
    HSK_HS --|derive_master_secret|--> MS["Master Secret"]
    MS --|derive_client_application_traffic_secret|--> CATS["Client Application Traffic Secret"]
    MS --|derive_server_application_traffic_secret|--> SATS["Server Application Traffic Secret"]
    MS --|derive_exporter_secret|--> EXPS["Exporter Secret"]
    MS --|derive_resumption_master_secret|--> RMS["Resumption Master Secret"]
    CTS --> AD_E["Early Application Data"]
    CHTS --> AD_H_C["Client Handshake Data"]
    SHTS --> AD_H_S["Server Handshake Data"]
    CATS --> AD_A_C["Client Application Data"]
    SATS --> AD_A_S["Server Application Data"]

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

以前のセッション情報(PSK: Pre-Shared Key)が利用可能な場合、TLS 1.3は0-RTTモードをサポートします。これにより、クライアントは最初のClientHelloメッセージとともに暗号化されたアプリケーションデータを送信でき、実質的にハンドシェイクによる遅延をゼロにすることが可能です。

sequenceDiagram
    participant C as クライアント
    participant S as サーバー
    C -> S: ClientHello (psk_key_exchange_modes, pre_shared_key, early_data) + [Early Application Data]
    S -- >C: ServerHello (psk_key_exchange_modes, pre_shared_key)
    S -- >C: EncryptedExtensions
    S -- >C: Certificate (サーバー証明書, オプション)
    S -- >C: CertificateVerify (証明書の署名検証, オプション)
    S -- >C: Finished
    C -> S: Finished
    C -> S: [Application Data] (暗号化済み)
    S -> C: [Application Data] (暗号化済み)

レコードプロトコル

TLS 1.3のレコードプロトコルは、ハンドシェイクメッセージやアプリケーションデータをカプセル化し、暗号化と認証を提供します。基本的なレコード構造は TLSPlaintextTLSCiphertext で定義されます。

TLSPlaintext
  ContentType: 8 bits (例: handshake, application_data)
  LegacyRecordVersion: 16 bits (TLS 1.3では常に 0x0301)
  Length: 16 bits (Payloadのバイト長)
  Payload: Length * 8 bits (実際のデータ)

TLSCiphertext
  ContentType: 8 bits (常に 0x17 (application_data) に設定され、実際のタイプはPayload内部に格納)
  LegacyRecordVersion: 16 bits (常に 0x0301)
  Length: 16 bits (Payloadのバイト長)
  Payload: Length * 8 bits (AEAD暗号化されたデータ)

TLSPlaintextPayload 部分にハンドシェイクメッセージやアプリケーションデータが格納されます。TLSCiphertext は、TLSPlaintext にAEAD (Authenticated Encryption with Associated Data) 暗号化を適用したもので、ContentType が固定されることで、外部からは暗号化されたデータの種類を推測しにくくし、プライバシーを向上させています [1, Section 5.1]。

既存プロトコルとの比較 (TLS 1.2 vs TLS 1.3)

項目 TLS 1.2 TLS 1.3
ハンドシェイク回数 2-RTT (初回接続) / 1-RTT (再開) 1-RTT (初回接続) / 0-RTT (再開, 特定条件で)
鍵交換方式 RSA、DHE、ECDHEなど選択可能 DHE/ECDHEのみ(PFS強制)
暗号スイート 多数の脆弱なアルゴリズムを含む 強固なAEAD暗号 (AES-GCM, ChaCha20-Poly1305) に限定
ハンドシェイクメッセージの暗号化 ほとんどのメッセージが平文 多くのメッセージ (EncryptedExtensions以降) が暗号化
セッション再開 Session IDs、Session Tickets Pre-Shared Key (PSK) のみ
非推奨/削除機能 RC4、SHA-1、MD5、CBCモード、RethNegotiation 上記の脆弱な機能が全て削除

セキュリティ考慮事項

TLS 1.3は、セキュリティを大幅に向上させていますが、いくつかの重要な考慮事項があります。

  • リプレイ攻撃 (0-RTT): 0-RTTデータは、ネットワーク上で傍受され再送されることで、サーバーに意図しない副作用を引き起こす可能性があります [1, Section 8.4]。例えば、ユーザー登録や商品購入のような冪等性のない操作が0-RTTデータに含まれると、攻撃者によって複数回実行されるリスクがあります。アプリケーション層での対策(ワンタイムトークン、シーケンス番号など)が必須であり、サーバーは0-RTTデータに対して厳格なリプレイ保護メカニズムを実装する必要があります。

  • ダウングレード攻撃からの保護: TLS 1.3は、古いバージョンのプロトコルへのダウングレード攻撃を防ぐためのメカニズムを内蔵しています [1, Section 4.1.3]。ServerHello.randomフィールドに特定の値を埋め込むことで、攻撃者がハンドシェイクメッセージを改ざんしてTLS 1.2などへダウングレードさせようとする試みを検知します。

  • キー更新 (KeyUpdate メッセージ): クライアントとサーバーは、ハンドシェイク後も任意でKeyUpdateメッセージを交換し、新しい秘密鍵を導出することができます [1, Section 4.6.3]。これにより、長時間のセッションにおいても、単一の鍵で保護されるデータ量を制限し、前方秘匿性をさらに強化します。鍵更新を定期的に行うことで、もし現在のセッション鍵が漏洩した場合でも、それ以降の通信の安全性を確保できます。

  • 前方秘匿性 (PFS) の強制: TLS 1.3では、DHEまたはECDHE鍵交換が必須となり、これによりPFSが強制されます。サーバーの長期的な秘密鍵(例:RSA証明書鍵)が将来的に漏洩しても、過去の通信内容が解読されることはありません。

  • 認証情報の秘匿: 多くのハンドシェイクメッセージが暗号化されるため、中間者攻撃者がTLSネゴシエーション中に交換される情報からエンドポイントの能力(サポートする拡張など)を推測することが困難になります。

実装メモ

TLS 1.3を効率的かつ安全に実装するためには、プロトコルの詳細だけでなく、OSやネットワークスタックとの連携も考慮する必要があります。

  • MTUとPath MTU (PMTU): TLSレコードの最大ペイロードサイズは16384バイトですが、基盤となるトランスポート層(TCPなど)のPath MTU (PMTU) を考慮することが重要です。大きなTLSレコードはIP層でフラグメント化される可能性があり、これはネットワークのパフォーマンスを低下させ、パケットロスの原因となることがあります。アプリケーション層で送信するデータチャンクのサイズを調整するか、TCP MSS (Maximum Segment Size) のネゴシエーションを適切に行い、PMTUの制約内でTLSレコードを構成するようにします。

  • HOL Blocking回避: TLS自体はアプリケーション層のHOL (Head-of-Line) Blockingを直接引き起こすわけではありませんが、TLSレイヤーでの非効率な処理は、上位プロトコル(HTTP/2やHTTP/3)のパフォーマンスに影響を与えます。非同期I/Oやマルチスレッド処理を適切に利用し、TLSレコードの暗号化・復号、ハンドシェイクメッセージの処理が、アプリケーションデータのフローを阻害しないように設計することが重要です。

  • キュー制御とバッファ管理: 特に高負荷環境では、受信したTLSレコードや送信待ちのアプリケーションデータを適切にキューイングし、バッファを管理することが不可欠です。メモリフットプリントとスループットのバランスを取りながら、DoS攻撃耐性を考慮したバッファサイズとキューの深さを設定する必要があります。

  • 優先度付け: ハンドシェイクメッセージ(特にFinished)は、セキュリティ上、タイムリーに処理される必要があります。実装においては、ハンドシェイクメッセージとアプリケーションデータトラフィックの間で適切な優先度付けを行い、ハンドシェイクの完了を遅らせないようにすることが望ましいです。

まとめ

TLS 1.3は、以前のバージョンから大幅に進化し、Web通信の高速化とセキュリティ強化を両立させた現代的なプロトコルです。1-RTTハンドシェイクによる低遅延化、0-RTTモードによるさらなる高速化、そして脆弱な暗号スイートの排除とPFSの強制は、インターネットの信頼性とパフォーマンスを向上させる上で不可欠な要素です。ネットワークエンジニアとして、このプロトコルの詳細な動作、特にセキュリティ上の考慮事項(0-RTTのリプレイ攻撃対策など)と実装上の注意点を理解することは、安全で高性能なシステムを構築するために極めて重要です。RFC 8446を深く理解し、常に最新のベストプラクティスを適用することで、堅牢な通信環境を提供することができます。


[1] T. Rescorla, ed., “The Transport Layer Security (TLS) Protocol Version 1.3”, RFC 8446, DOI 10.17487/RFC8446, August 2018, https://www.rfc-editor.org/rfc/rfc8446. (最終確認日: 2024年7月30日)

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

コメント

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