「HTTPSにすれば安全」の裏側で何が起きているか
Webサービスを作る時、「HTTPSで通信すれば暗号化されるから安全」とは知っている。でも、ブラウザとサーバーの間で具体的に何が行われて「安全」になるのかを説明できるでしょうか?
この「安全な通信を確立するまでの手続き」がTLSハンドシェイクです。普段の開発で意識する場面は少ないですが、証明書エラーのトラブルシュート、パフォーマンスチューニング、セキュリティ設計の場面で知っているかどうかで対応速度がまるで変わります。
本記事では、TLSハンドシェイクの流れを図解し、なぜこの手順が必要なのかを整理します。
そもそもHTTPSって何が「S」なのか
HTTPSはHTTP + TLS(Transport Layer Security)です。HTTPの通信内容をTLSで暗号化して送受信する仕組み。以前はSSL(Secure Sockets Layer)と呼ばれていましたが、現在はTLSが後継規格です。「SSL証明書」という呼び方が今も残っていますが、実際に使われているのはTLSです。
TLSが保証するのは以下の3つです。
- 暗号化:通信内容を第三者に読まれない
- 改ざん検知:通信内容が途中で書き換えられていないことを保証
- 認証:通信相手が本物のサーバーであることを証明書で確認
この3つを確立するのがTLSハンドシェイクです。
TLSハンドシェイクの流れを図で理解する
TLS 1.3(現在の主流)のハンドシェイクを簡略化すると、以下の流れになります。
クライアント(ブラウザ) サーバー
| |
| ① ClientHello |
| 「TLS 1.3で話したい。 |
| 使える暗号方式はこれとこれ。 |
| 鍵交換用のデータも送るね」 |
| -----------------------------------→ |
| |
| ② ServerHello + 証明書 + 鍵交換 |
| 「OK、この暗号方式で行こう。 |
| うちの証明書はこれ。 |
| 鍵交換用のデータも返すよ」 |
| ←----------------------------------- |
| |
| 【ここで双方が共通鍵を計算】 |
| (お互いの鍵交換データから導出) |
| |
| ③ 暗号化通信開始 |
| 「以降はこの共通鍵で暗号化するね」 |
| ←---------------------------------→ |
| |
| ④ HTTPリクエスト/レスポンス |
| (すべて暗号化された状態でやり取り) |
| ←---------------------------------→ |TLS 1.3ではわずか1往復(1-RTT)でハンドシェイクが完了します。以前のTLS 1.2では2往復必要だったので、接続速度が改善されています。
各ステップで何が起きているか
① ClientHello:ブラウザが「こんにちは、TLS 1.3で話しましょう。使える暗号方式の一覧と、鍵交換用の公開値を送ります」と挨拶する。
② ServerHello + 証明書:サーバーが暗号方式を選び、自分のSSL証明書(公開鍵を含む)と鍵交換用の公開値を返す。ブラウザは証明書を検証して「このサーバーは本物か?」を確認する。
共通鍵の計算:双方が交換した公開値から、同じ共通鍵(セッションキー)を計算する。この共通鍵自体はネットワーク上を流れないため、盗聴されても共通鍵は分からない。
③④ 暗号化通信:以降のHTTP通信はすべてこの共通鍵で暗号化されます。
なぜこの手順が必要なのか ― 共通鍵と公開鍵の使い分け
「最初から共通鍵で暗号化すればいいのでは?」と思うかもしれません。でも、共通鍵をどうやって安全に相手に渡すかが問題になります。暗号化されていない経路で共通鍵を送ったら、盗聴されて意味がない。
そこで使われるのが公開鍵暗号(鍵交換)です。
| 暗号方式 | 特徴 | TLSでの役割 |
|---|---|---|
| 公開鍵暗号 | 安全だが処理が重い | ハンドシェイク時の鍵交換・証明書検証に使用 |
| 共通鍵暗号 | 処理が軽く高速 | ハンドシェイク後のデータ暗号化に使用 |
TLSは「公開鍵暗号で安全に共通鍵を共有し、以降は高速な共通鍵暗号でデータをやり取りする」というハイブリッド方式です。セキュリティと速度を両立するための設計です。
開発で役立つ場面
証明書エラーのトラブルシュート
「このサイトは安全ではありません」エラーの原因は、ハンドシェイクの②で証明書の検証に失敗していること。証明書の期限切れ、中間証明書の不足、ドメイン名の不一致など、ハンドシェイクの流れを知っていれば原因の切り分けが速くなります。
# 証明書の情報を確認(OpenSSL)
openssl s_client -connect example.com:443 -servername example.com
# 証明書の有効期限だけ確認
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -datesパフォーマンスへの影響
ハンドシェイクにはネットワークの往復時間(RTT)がかかります。TLS 1.3は1-RTTで済みますが、TLS 1.2だと2-RTTかかる。海外サーバーへの接続でRTTが100msなら、ハンドシェイクだけで100〜200msの遅延。HTTP/2やセッション再開(0-RTT)を活用することで、この遅延を軽減できます。
ローカル開発でのHTTPS
ローカル開発でHTTPSが必要な場面(Service Worker、Secure Cookieなど)では、mkcertでローカル用の証明書を生成するのが定番です。
# mkcertでローカル用の証明書を作成
brew install mkcert
mkcert -install
mkcert localhost 127.0.0.1TLS・セキュリティの理解を深める関連記事
- 「全部POSTでよくない?」がモヤる人へ|HTTPメソッド使い分けの正論と現場のリアル – HTTPの上に乗るTLS、そのHTTPの設計判断を深掘り
- リリース前に必ず確認!バイブコーディング&非エンジニア向けWebアプリ安全チェックリスト – HTTPS強制やセキュリティヘッダーを含むリリース前チェック
- 「それ、上げちゃダメ!」GitHub管理で絶対守るべきセキュリティルールと対処法 – 秘密鍵や証明書ファイルをGitにコミットしないための基本
- コマンドラインの基本と活用方法【初心者エンジニア向け】 – opensslコマンドやcurlでの証明書確認に必要なCLI操作
まとめ:ハンドシェイクは「安全に鍵を共有する」ための手続き
- HTTPSはHTTP + TLS。暗号化・改ざん検知・認証の3つを提供する
- TLSハンドシェイクは、公開鍵暗号で安全に共通鍵を共有し、以降の通信を高速な共通鍵暗号で行うための手続き
- TLS 1.3は1往復で完了。TLS 1.2の2往復から高速化されている
- 証明書エラーのトラブルシュートには、ハンドシェイクのどのステップで失敗しているかの理解が効く
- ローカル開発のHTTPSには
mkcertが便利
普段の開発ではTLSを意識する場面は少ないかもしれません。でも、証明書エラーが起きた時、パフォーマンスの遅延を調査する時、セキュリティレビューで質問された時 ― 「裏側で何が起きているか」を知っている人は、対応の質とスピードがまるで違います。一度理解してしまえば一生使える知識なので、この機会にハンドシェイクの流れを頭に入れておいてください。
