TLSの基礎
HTTPSの土台となるTLSが何を保証し、ハンドシェイクでどう通信を確立するかを理解する
TL;DR
- TLSは通信の 暗号化・認証・改ざん検知 の3つを提供するプロトコル
- ハンドシェイクで証明書の検証と共通鍵の交換を行い、その後の通信を共通鍵で暗号化する
- TLS 1.3はラウンドトリップが1回に削減され、古い暗号スイートが排除されたモダンな仕様
TLSが提供する3つの保証
| 保証 | 内容 |
|---|---|
| 機密性(Confidentiality) | 通信内容を第三者に読まれない(暗号化) |
| 完全性(Integrity) | 通信内容が途中で改ざんされていない(MACによる検証) |
| 認証(Authentication) | 接続先が本当に意図したサーバーである(証明書による検証) |
HTTPSは「HTTP over TLS」。TLSなしのHTTPはこれらを一切保証しない。
証明書とは何か
TLSで使われる証明書(X.509証明書)は、「この公開鍵はこのドメインのものである」をCAが保証した文書。
証明書に含まれる主な情報:
- Subject(CN / SAN にドメイン名や IP が入る)
- 公開鍵
- 有効期限
- 発行者(CA)の情報
- CAの署名クライアントは証明書を受け取ったとき、次を確認する。
- CAの署名が正しいか(信頼できるCAが署名しているか)
- 有効期限が切れていないか
- 証明書のSAN(Subject Alternative Name)が接続先ドメインと一致しているか
証明書の作り方・CSRの用途指定については mTLS・RSA・CSRの基礎 を参照。
TLSハンドシェイクの流れ(TLS 1.3)
TLS 1.3では 1-RTT(1回のラウンドトリップ)で接続が確立する。
Client Server
| |
|── ClientHello ───────────────────→ | 使える暗号スイートと鍵共有パラメータを送る
| |
| ←───────────── ServerHello ────── | 選んだ暗号スイートと鍵共有パラメータを返す
| ←─────────── {Certificate} ────── | サーバー証明書(暗号化済み)
| ←──────── {CertificateVerify} ── | 証明書の署名(秘密鍵で署名して本人証明)
| ←──────────────── {Finished} ──── | ハンドシェイク完了
| |
|── {Finished} ──────────────────→ | クライアント側も完了
| |
|── アプリケーションデータ(暗号化)────→ |{} は暗号化済みメッセージを表す。TLS 1.3では証明書も暗号化されて送られる。
TLS 1.2との主な違い
| 項目 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| ハンドシェイク | 2-RTT | 1-RTT(0-RTTも可能) |
| 鍵交換 | RSA鍵交換も可(前方秘匿性なし) | ECDHE のみ(前方秘匿性を強制) |
| 廃止された暗号スイート | MD5, SHA-1, RC4, 3DES など | 自動的に排除 |
| 証明書の暗号化 | なし(平文で送信) | あり |
共通鍵はどうやって共有するか(鍵交換)
ハンドシェイクで最終的に双方が同じ共通鍵を持つ必要があるが、その鍵を直接ネットワークで送ると盗まれる。
TLS 1.3では ECDHE(楕円曲線ディフィー・ヘルマン鍵交換) を使う。
双方が鍵ペアを生成し、互いの公開鍵を交換する。
→ それぞれの秘密鍵と相手の公開鍵から、同じ値(マスターシークレット)が計算できる。
→ マスターシークレットを元に共通鍵を導出する。秘密鍵は通信に流れないため、通信を傍受されても共通鍵を導出できない(前方秘匿性)。
証明書チェーン(信頼の連鎖)
ブラウザや OSがあらかじめ信頼しているのは ルートCA だけ。
実際のサーバー証明書は、中間CA(Intermediate CA)を経由して発行されることが多い。
ルートCA(OSが信頼)
└── 中間CA(ルートCAが署名)
└── サーバー証明書(中間CAが署名)クライアントはサーバーから送られた証明書チェーンをたどり、最終的にルートCAに到達できれば信頼できると判断する。
SNI(Server Name Indication)
1つのIPアドレスに複数のドメインを持つサーバーでは、TLSハンドシェイクの時点でクライアントが接続先ドメインを通知する必要がある。これが SNI。
ClientHello に server_name 拡張を含める
→ サーバーは対応するドメインの証明書を選んで返すSNIがないと、サーバーはどのドメイン向けの証明書を返せばよいか判断できない。
よくある落とし穴
証明書にSANがなく接続が失敗する
古い証明書にはSANがなくCNだけのものがある。モダンなブラウザはCNを無視しSANで検証するため接続が拒否される。
中間CA証明書をサーバーに設定し忘れる
サーバー証明書だけ設定しても、クライアントが中間CAを知らないと検証に失敗することがある。証明書チェーンをまとめたフルチェーン(fullchain.pem)を設定する。
TLS 1.2以下だけ対応した古いクライアントとの接続
セキュリティ要件によりTLS 1.3のみを許可する設定にすると、古いクライアントが接続できなくなる。互換性が必要な場合はTLS 1.2も許可しつつ、弱い暗号スイートを無効化する。
証明書の有効期限切れ
Let's Encryptは90日で期限が切れる。自動更新(certbot renew等)の仕組みを必ず用意する。