はじめに
この記事では、クライアントと Web サーバーの間で HTTPS を使った通信が確立するまでのおおまかな流れをできるだけ分かりやすく、TLS に焦点をあてて解説します。
// (2024/10/21) 前提のセクションを @juner さんの助言により追記しました。
もくじ
前提(注意点)
この記事で扱う内容は 以下のバージョンを対象としています。
- HTTP/1.0
- HTTP/1.1
- HTTP/2
HTTP/3
HTTP/3 ではコネクションの確立の際に TCP ではなく QUIC(Quick UDP Internet Connections)というプロトコルを使用します。QUIC は TLS 1.3を組み込んだ、UDP を基盤としたプロトコルになります。したがって、HTTP/2 以前と HTTP/3 では通信の方法に違いがあるのでご注意ください。
HTTP/2 以前では TCP コネクションの確立 ⇒ TLS ハンドシェイクする、という流れだったのが、HTTP/3 の QUIC ではそれらをまとめてやっちゃう、みたいなイメージですね
HTTPS とは
HTTPS(HyperText Transfer Protocol Secure)は、クライアントとサーバー間の通信を暗号化することで安全性とプライバシーを確保する通信方式です。HTTPS そのものは独自のプロトコルではなく、その実態は TLS で通信経路をセキュアにした状態で行う HTTP 通信 になります。
(ちなみにHTTPSはHyper Text Transfer Protocol over SSLじゃないらしいよ!)
流れを追うための前提知識
HTTP とは
HTTP(HyperText Transfer Protocol)プロトコルの利用は、ほとんどの場合Webサーバーに向けた接続要求です。
- HTTPはWebブラウジングの基盤となるプロトコル
- クライアント(通常はウェブブラウザ)がサーバーに HTTP リクエストを送信し、サーバーが HTTP レスポンスを返す
- Web サーバーだけでなく API サーバー、プロキシサーバー、キャッシュサーバーなどが HTTP リクエストを受け取る場合もある
TLS について
TLS の概要
TLS(Transport Layer Security)はインターネット上でデータを安全にやり取りするためのプロトコル になります。元々は SSL(Secure Sockets Layer)という名前でしたが、より安全性を高めるため(脆弱性が発見されるたび)に改良されていく過程で、名前が TLS に変わりました。
なので SSL/TLS や SSL とは言ってるけど指しているのは TLS プロトコルです、みたいなこともあります。
TLS の目的
TLS の主な目的は以下の3つです。
- データの暗号化:通信データを暗号化することで、第三者に内容を読み取られないようにする(かつ、通信が盗聴されても暗号化されたデータの内容を理解することができない)
- データの完全性の確保:通信データが途中で改ざんされていないことを保証する
- 通信相手の認証:通信相手が正当なサーバーやクライアントであることを確認し、なりすましを防ぐ
TLS の実現方法(TLS ハンドシェイク)
クライアント(例えばブラウザ)とサーバー(例えばウェブサイト)の間で、TLS ハンドシェイクというプロセスを踏むことによってセキュアな通信を確立します。おおまかに、このプロセスでは通信する二者間(通常はクライアントとサーバー)で以下を行います。
- 互いの認識:クライアントとサーバーが接続要求と応答を通じて互いを識別する
- SSL 証明書の検証:サーバーの証明書をクライアントが検証することで、通信相手が信頼できることを確認する
- 暗号アルゴリズムの決定:双方が使用する暗号化アルゴリズムを合意する
- セッション鍵の合意:安全な通信のためのセッション鍵を生成し、共有する
HTTPS 通信を開始するまでのフロー
Web ブラウザに入力された URL からプロトコルとドメイン名を取得し、接続先の通信機器と HTTPS 通信を始めるまでのおおまかなフローを以下に示します。
IP アドレスの特定(DNS ルックアップ / 名前解決)
URL からドメイン名を抽出し、DNS サーバーに問い合わせ(キャッシュが見つかればそれを使う)を行った結果、ドメインと対応する IP アドレスが取得できます。
ポート番号の決定
デフォルトポート番号を使用する場合:
ユーザーがウェブブラウザで https://www.example.com
と入力すると、特に指定がなくてもブラウザは自動的に HTTPS のウェルノウンポートである 443 番ポートに接続を試みます。
URL 内で明示的にポート番号を指定された場合:
URL 内で明示的にポート番号を指定することで、任意のポート番号を指定できます。例えば https://www.example.com:8443
と入力するとブラウザは 8443 番ポートに接続を試みます。
コネクションの確立
上記までで判明した IP アドレスとポート番号によって、通信したい相手(機器)とアプリケーションのポートが分かりました。ここでは、クライアントとその通信の宛先であるサーバー(あるいはロードバランサー)との間で TCP の 3 ウェイハンドシェイクを行い、コネクションを確立します。
データの転送前に通信の安定性を確保するための 3 ウェイハンドシェイクですが、当然このプロセスでは TCP/IP モデルの各レイヤをギュンギュン通ります。
Step1.クライアントからサーバー(SYN パケットの送信)
クライアント側の各レイヤ:
No | レイヤ | 概要 |
---|---|---|
1 | アプリケーション層 | データなし(※ 後述) |
2 | トランスポート層(TCP) | TCP ヘッダーに SYN フラグをセットし、新しい TCP セグメントを準備 |
3 | インターネット層(IP) | TCP セグメントを IP パケットにカプセル化し、送信元IPアドレスと宛先IPアドレスを含める |
4 | ネットワーク インターフェース層 (Ethernet) |
IP パケットをイーサネットフレームにカプセル化し、物理ネットワークへ送信 |
サーバー側の各レイヤ:
No | レイヤ | 概要 |
---|---|---|
5 | ネットワーク インターフェース層 (Ethernet) |
イーサネットフレームを受信、上位層に受け渡し |
6 | インターネット層(IP) | イーサネットフレームから IP パケットを取り出し、送信元IPアドレスと宛先IPアドレスを確認 |
7 | トランスポート層(TCP) | IP パケットから TCP セグメントを取り出し、SYN フラグがセットされていることを確認 |
Step2.サーバーからクライアント(SYN-ACK パケットの送信)
サーバー側の各レイヤ:
No | レイヤ | 概要 |
---|---|---|
8 | アプリケーション層 | データなし(※ 後述) |
9 | トランスポート層(TCP) | SYN と ACK フラグをセットした TCP セグメントを準備 |
10 | インターネット層(IP) | SYN-ACK セグメントを IP パケットにカプセル化し、送信元IPアドレスと宛先IPアドレスを含める |
11 | ネットワーク インターフェース層 (Ethernet) |
IP パケットをイーサネットフレームにカプセル化し、物理ネットワークへ送信 |
クライアント側の各レイヤ:
No | レイヤ | 概要 |
---|---|---|
12 | ネットワーク インターフェース層 (Ethernet) |
イーサネットフレームを受信、上位層に受け渡し |
13 | インターネット層(IP) | イーサネットフレームから IP パケットを取り出し、送信元IPアドレスと宛先IPアドレスを確認 |
14 | トランスポート層(TCP) | IP パケットから TCP セグメントを取り出し、SYN-ACK フラグがセットされていることを確認 |
Step3.クライアントからサーバー(ACK パケットの送信)
クライアント側の各レイヤ:
No | レイヤ | 概要 |
---|---|---|
15 | アプリケーション層 | データなし(※ 後述) |
16 | トランスポート層(TCP) | ACK フラグをセットした TCP セグメントを準備 |
17 | インターネット層(IP) | ACK セグメントを IP パケットにカプセル化し、送信元IPアドレスと宛先IPアドレスを含める |
18 | ネットワーク インターフェース層 (Ethernet) |
IP パケットをイーサネットフレームにカプセル化し、物理ネットワークへ送信 |
サーバー側の各レイヤ:
No | レイヤ | 概要 |
---|---|---|
19 | ネットワーク インターフェース層 (Ethernet) |
イーサネットフレームを受信、上位層に受け渡し |
20 | インターネット層(IP) | イーサネットフレームから IP パケットを取り出し、送信元IPアドレスと宛先IPアドレスを確認 |
21 | トランスポート層(TCP) | IP パケットから TCP セグメントを取り出し、ACK フラグがセットされていることを確認、接続を確立 |
これによって、アプリケーション間でデータを確実に送受信するための論理的な通信経路(コネクション)が整います。
※ ちなみに SYN は Synchronize の略であり、ACK は Acknowledge の略
※ アプリケーション層のデータがないのは、3 ウェイハンドシェイクが TCP 接続を確立する段階のため、実際のデータ通信がまだ開始されていないからです。(アプリケーション層のデータは TCP 接続が正常に確立された後に送信される)そもそも接続の確立と接続の確認のみを目的としているので、アプリケーション層のデータを含む必要がないのです。
TLS を利用したセッションの確立
コネクションが確立しているので、すでに HTTP リクエストを送信することが可能になっています。なのですが、HTTPS の場合はここで TLS ハンドシェイクを行いセキュアな通信を確立しにいきます。
Step1.Client Hello
- クライアントが最初にサーバーに対して TLS/SSL 接続を開始するためのメッセージを送る
- このリクエストにはクライアントが対応能な TLS バージョンや利用可能な暗号スイート(暗号アルゴリズムの組み合わせ)、クライアントランダムデータ(ランダムな32バイトのデータ)などが含まれる
Step2.Sever Hello
- Client Hello に応答し、決定した TLS バージョンと暗号スイートの情報、サーバーランダムデータ(ランダムな32バイトのデータ)を返す
- クライアントとサーバーが共通の TLS バージョンを見つけられない場合、ハンドシェイクは失敗する
- クライアントが提案する暗号スイートの中にサーバーがサポートするものがない場合もハンドシェイクは失敗する
Step3.Server Certificate
- Sever Hello の直後に、サーバーは公開鍵を含む自身のデジタル証明書を送信する
- この証明書は認証局(Certification Authority:CA)によって発行されており、以下の2つの機能を有している
- 暗号化通信:証明書にはサーバーの公開鍵が含まれており、この公開鍵を使って暗号化通信が行われる
- 実在の証明:そのサーバーが実際にそのドメイン名を所有していることを証明するもの
Step4.証明書の検証
- クライアントは送られてきたサーバーのデジタル証明書を、発行元の認証局(CA)に確認、検証する
- これにより、これからやりとりをするサーバーがそのドメインの所有者であることを確認する
Step5.セッション鍵の共有
-
サーバー側:
- クライアントに追加の鍵交換情報を送ることがある(※ あんまりないらしい)
- サーバーがデジタル証明書を保持していない場合
- Server Certificate で送信したデジタル証明書に公開鍵が含まれない場合など
- 初期ハンドシェイクの完了(Server Hello Done メッセージ)をクライアントに送信
- クライアントに追加の鍵交換情報を送ることがある(※ あんまりないらしい)
-
クライアント側:
- サーバーにセッション鍵を生成するための情報を送信(Client Key Exchange メッセージ)
Step6.セッション鍵生成
クライアントとサーバーは互いに以下のプロセスを踏みます。
- 共通のプレマスターシークレット(クライアントとサーバーが一時的に共有する秘密情報)を作成
- Client/Server Hello で交換していたランダムデータ(ランダムな32バイトのデータ)を用意
- プレマスターシークレット・クライアントランダムデータ・サーバーランダムデータを結合してマスターシークレットを生成
- マスターシークレットを基に、実際のデータの暗号化やメッセージ認証に使われるセッション鍵を生成(生成方法は TLS プロトコルで厳密に定義されている)
クライアントとサーバーの手元には、お互いに交換しあって揃えた 3 つのメッセージ(プレマスターシークレット・クライアントランダムデータ・サーバーランダムデータ)があります。で、セッション鍵の元になるマスターシークレットは 3 つのメッセージを結合した形であると。つまり、 結果的にそのマスターシークレットから生成したセッション鍵はクライアントとサーバーで同じものになる!!
こうして生成されたセッション鍵は、クライアントとサーバーが共通の秘密情報を基にしているため、両者は同じセッション鍵を持つことができるよーて。このセッション鍵を使って以降の通信データが暗号化され、安全に通信が行われるわけです。
Step7.ハンドシェイク終了
- クライアントとサーバーはお互いに、セッション鍵によって暗号化された Finished メッセージを交換する(ハンドシェイクの完了を確認)
- 以降はこのセッション鍵を使って暗号化されるので、安全に通信ができるようになる
セキュアな状態での HTTP リクエスト
はい、TLS ハンドシェイクの完了をもって場は整いましたのでこれ以降は普通の HTTP 通信です。
例えば、ブラウザに https://qiita.com/_mi/items/0e37def5b155dfc02aab
を打ち込んだ時に返ってくる Response Headers には以下の内容が含まれます。
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://qiita.com
Date: Sun, 20 Oct 2024 03:29:12 GMT
...
HTTP/1.1
とHTTP のバージョン情報が Response に記載されていることから、この通信は HTTP プロトコルに基づいて行われていることが分かります。こんな感じで通常の HTTP リクエスト/レスポンスが行われるわけですが、TLS で確立されたセキュアなセッションを使用して通信が行われるので「より安全な HTTP 通信」です。これを HTTPS と呼ぶわけですね~!
中身それ自体は HTTP なので、HTTP のセッション管理やユーザー識別に使用する Cookie なども主に HTTP プロトコルを通じてやり取りされます。
まとめ
タイトル通り、HTTPS は TLS プロトコルによって提供されるセキュアな接続の上での HTTP 通信です!HTTPS の通信を確立するためのステップは以下になります。
- DNS で IP アドレスを取得
- ブラウザからポート番号を取得
- IP アドレスとポート番号をもとに、通信機器を特定する
- 通信相手と 3 ウェイハンドシェイクしてコネクションを確立
- TLS ハンドシェイクでセキュアなセッションの確立
- 通常の HTTP リクエスト/レスポンス
全体としてざっくりとした説明でしたが、記事を読んで HTTPS 通信のイメージが湧いてたらいいなぁと思います!
おわりに
ブラウザに URL を打ち込んだとき、クライアントとサーバーの間をデータが何往復もしてんの冷静に考えてヤバいよね。
参考