新人エンジニアのための今度こそ理解するSSL/TLS超入門
はじめに
Web開発やインフラに関わっていると、必ず目にするのがHTTPSです。
今や「HTTPではなくHTTPSが当たり前」という世界になっています。
一方で、こんな経験はありませんか?
- 証明書エラーが出たけど、よく分からず無視した
- 「オレオレ証明書」と言われてもピンとこない
- HTTPSなのに「安全ではありません」と表示されて混乱した
SSL/TLSは使うことは簡単ですが、何が起きているかを理解するのは意外と難しい技術です。
特に新人エンジニアのうちは、
「HTTPS = 暗号化されていて安全」
というイメージだけで通り過ぎてしまいがちです。
しかし実際の SSL/TLS は、暗号化だけでなく「相手が本物かどうか」を確認する仕組みでもあります。
この記事で扱うこと・扱わないこと
この記事では、SSL/TLS を 次の視点 から説明します。
- TLSは何を守っているのか
- サーバーはどうやって「本物」と判断されているのか
- なぜ証明書エラーが出るのか
一方で、以下のような内容は 今回は深掘りしません。
- 公開鍵/秘密鍵の説明
- サーバー証明書の作成方法
- 中間CAについて
- RFCレベルの仕様解説
「仕組みを理解するために最低限必要な話」だけに絞り、
現場で起きがちな疑問がつながることをゴールにします。
SSL/TLS は何を守っているのか
SSL/TLSというと、
「通信を暗号化する仕組み」
が一番最初に頭に浮かぶかもしれません。
これはSSL/TLSが守っているものの1つですが、それだけではありません。
SSL/TLSが守っているものは、次の3つです。
通信内容の盗聴を防ぐ(暗号化)

まず一番イメージしやすいのが 暗号化 です。
HTTP の通信内容は、ネットワーク上をそのままの文字列で流れます。
そのため、途中で盗み見られると
- パスワード
- APIトークン
- 個人情報
が簡単に読めてしまいます。
SSL/TLSを使うと、通信内容は暗号化されるため、
- 途中で見られても中身は分からない
- 第三者は意味のあるデータを取得できない
状態になります。
「盗聴対策」が1つ目の役割です。
通信内容の改ざんを防ぐ(完全性の保証)
盗聴だけでなく、途中で内容を書き換えられるのも危険です。
例えば
- APIのレスポンスを書き換えられる
- フォームの送信内容を書き換えられる
- ダウンロードファイルに不正なデータを混ぜられる
SSL/TLS では、
「受け取ったデータが、送信時と同じ内容か」
を確認できる仕組みがあります。
これにより通信の途中で1文字でも変わると検出でき、改ざんされた通信は破棄されます。
「改ざん防止」が2つ目の役割です。
接続先のサーバーが本物かを確認する(認証)

SSL/TLS は単なる暗号化ではなく、
「今つないでいるサーバーは、本当にそのサービス本人か?」
を確認しています。
例えば、URLにhttps://example.comと入力したとき、
本当にexample.comのサーバーなのか
見た目だけ似せた偽サーバーではないかをTLSがチェックします。
この確認に使われるのがサーバー証明書です。
「なりすまし防止」が3つ目の役割です。
サーバー証明書とは何か
HTTPSの仕組みを理解するうえで、必ず登場するのが「サーバー証明書」です。
HTTPS通信をするには、サーバーに「サーバー証明書」をもたせる必要があります。
サーバー証明書の役割
サーバー証明書の役割は、ひとつにまとめるとこうです。
「このサーバーは、このドメインの正当な持ち主です」
ということを証明します。
SSL/TLS は、通信を始めるときに次の問いに答えようとします。
「今つなごうとしている相手は、本物か?」
この問いに答えるために使われるのがサーバー証明書です。
サーバー証明書に書かれていること
サーバー証明書の中には、主に次のような情報が入っています。
- この証明書が使われるドメイン名
- 有効期限
- 誰が発行したか(認証局 / CA)
- サーバーの公開鍵
重要なのは、証明書の中身は基本的に「公開情報」だという点です。
誰でもブラウザから内容を見ることができます。
例えばzenn.devの証明書は以下のようになっています。

サーバー証明書があれば信頼できるのか?
サーバー証明書は、誰でも作成できます。
ツールを使えば、数分で証明書ファイルを作ることも可能です。
そのため、
「サーバー証明書がある = 信頼できる」
というわけではありません。
信頼できるサーバー証明書とは、次の条件を満たすものです。
- 信頼された第三者が発行している
- その第三者を、端末(ブラウザや OS)が信頼している
この「信頼された第三者」が認証局(CA: Certificate Authority)です。
先程のzenn.devの証明書では「Google Trust Services」がCAとなります。
CAは、
「このサーバーは、このドメインの正当な持ち主です」
という確認を行ったうえで、証明書に電子署名を行い、サーバー証明書を発行します。
WebブラウザやOSには、あらかじめ
「信頼してよい認証局(CA)の証明書」
が登録されています。
ブラウザはサーバー証明書を受け取ると、
- この証明書は誰が発行したか?
- その発行元は信頼済みのCAか?
- 証明書の内容に問題はないか?
を確認します。
このチェックがすべて通ったとき、初めて「このサーバーは本物だ」と判断されます。
ポイントとしては
- WebブラウザやOSは「信頼してよいの認証局(CA)の証明書」を最初からもっている
- サーバー証明書はWebブラウザやOSが信用している認証局(CA)に作成してもらう必要がある
ということです。
自己署名証明書(オレオレ証明書)について
ここまでで、
- サーバー証明書は誰でも作れる
- 本物かどうかは「誰が保証しているか」が重要
という話をしてきました。
この流れで説明しておきたいのが自己署名証明書(オレオレ証明書)です。
自己署名証明書(オレオレ証明書)とは何か
自己署名証明書とは、第三者(認証局/CA)ではなく、サーバー自身が自分で署名した証明書のことです。
通常のサーバー証明書では認証局(CA)が自分の秘密鍵で署名し「このサーバーは本物です」と保証します。
一方、自己署名証明書ではサーバー自身が自分の秘密鍵で署名し「オレが本物です」と主張します。
当然、信頼できる認証局(CA)による署名ではないのでクライアントでは警告が出ます。

自己署名証明書を何につかうの?
最も多いのがローカル開発環境です。
- localhostでHTTPSを試したい
- CookieのSecure属性を検証したい
- HTTPS前提のAPIをローカルで動かしたい
このとき、「警告が出ることを理解したうえで」自己署名証明書を使うことがあるのです。
認証局(CA)の署名の仕組み
前の章で、
認証局(CA)は証明書に電子署名を行い、サーバー証明書を発行する
と書きました。
では実際に、CAは何をどうやって署名しているのかをもう一段深く見てみます。
ステップ① 証明書の中身からハッシュ値を作る
CAは、これから発行する証明書の内容(例:ドメイン名や有効期限など)を使って、ハッシュ値を計算します。
ハッシュ値には、次の特徴があります。
- 元のデータから必ず同じ結果が出る
- データが1文字でも変わると、結果が大きく変わる
- ハッシュ値から元のデータは復元できない

ステップ② ハッシュ値をCAの秘密鍵で署名する
次にCAは、計算したハッシュ値をCA自身の秘密鍵を使って署名します。
この「秘密鍵でハッシュ値を署名したデータ」が、証明書に含まれる電子署名です。
この署名はCAの公開鍵でのみ検証して、ハッシュ値を取り出すことができます。

ステップ③ 署名付き証明書がサーバー証明書
最終的に、証明書には次のものが含まれます。
- 証明書の中身(ドメイン名など)
- ハッシュアルゴリズムの情報
- CAの秘密鍵で署名されたハッシュ値
これがCA署名済みサーバー証明書です。
「サーバーは本物か?」はどうやって確認するのか
次に、サーバーから証明書を受け取ったブラウザ側の動きを見てみます。
ステップ① 証明書の中身からハッシュ値を計算する
ブラウザは、受け取った証明書の中身を使ってCAと同じハッシュアルゴリズムでハッシュ値を計算します。

ステップ② 電子署名を公開情報で検証する
次にブラウザは、証明書に付いている電子署名を検証します。
ブラウザにはCAの証明書があらかじめ入っているため、その証明書からCAの公開鍵を取り出して電子署名を検証できます。
すると、CAが署名したときのハッシュ値が取り出せます。

ステップ③ ハッシュ値を比較する
最後に、
- 自分で計算したハッシュ値
- 署名から取り出したハッシュ値
を比較します。
ここで一致していれば証明書は改ざんされていないCAが確かに署名したものとなります。
思い出してほしいのが、ハッシュの特性として1文字でも変更されると別物のハッシュ値が生成されることです。
もしも1文字でも証明書の内容が改ざんされたら、「自分で計算したハッシュ値」と「署名から取り出したハッシュ値」は一致しません。
一致しなければ内容が改ざんされているもしくは正しい署名ではないと判断されます。
よってサーバー証明書は正しくないため、サーバーは本物ではないということになります。
これでSSL/TLSの機能の1つであるなりすましを防止できますね。

TLSハンドシェイクについて
HTTPSの通信では、いきなりデータのやり取りが始まるわけではありません。
通信開始時に、TLSハンドシェイクと呼ばれる準備処理が行われます。
このハンドシェイクの目的は、次の2つです。
- 通信を暗号化するための準備
- 接続先サーバーが本物かどうかの確認
ここでは、TLSハンドシェイクを行い、
通信が暗号化されるまでの流れを大まかなステップで見ていきます。
TLSハンドシェイクの全体像 (TLS 1.2の場合)
TLS ハンドシェイクは、ざっくり言うと次の流れで進みます。
- クライアントが接続を開始する → 図①
- サーバーが証明書を送る → 図②③④
- クライアントが証明書を検証する → (クライアント側の処理)
- クライアントが共通鍵の元を作る → 図⑤
- サーバーの公開鍵で暗号化して送る → 図⑤
- サーバーが秘密鍵で復号する → (サーバー側の処理)
- 暗号化された通信が始まる → 図⑥⑦⑧⑨
下の図は実際の通信の詳細な流れです。

ステップ① クライアントが接続を開始する

ブラウザなどのクライアントは、HTTPSのURL にアクセスすると、
- 「これからTLSで通信したい」
- 「使える暗号方式はこれとこれ」
といった情報をサーバーに送ります。
ステップ② サーバーが証明書を送る

サーバーは、クライアントからの要求を受け取ると、
- サーバー証明書
- 使用する暗号方式に関する情報
をクライアントに返します。
ここで送られてくるサーバー証明書を使って、クライアントは次の確認を行います。
ステップ③ サーバー証明書の検証
クライアントは受け取ったサーバー証明書に対して、
- 証明書は有効期限内か
- 接続先のドメイン名と一致しているか
- 信頼できるCAが署名しているか
- 証明書の内容が改ざんされていないか
を確認します。
証明書の内容が改ざんされていないかのチェック方法は先程「「サーバーは本物か?」はどうやって確認するのか」にて説明しましたね。
ここで問題があれば、ブラウザに警告が表示されて通信は中断されるという挙動になります。
ステップ④ クライアントが共通鍵の元を作る

サーバー証明書の検証が成功すると、
クライアントはこれからの通信に使う 共通鍵の元(プリマスターシークレット) を作成します。
この「共通鍵の元」は、
- この通信専用に生成される
- 毎回ランダムに作られる
- 外部に知られてはいけない
という性質を持った秘密の値です。
この値をもとに、実際の通信暗号化用の共通鍵が作られます。
ステップ⑤ サーバーの公開鍵で暗号化して送る

クライアントは、ステップ④で作成した 共通鍵の元(プリマスターシークレット) をそのまま送ることはしません。
もしそのまま送って盗聴されてしまったら、盗聴者に共通鍵を作られてしまいますからね。
代わりに、サーバー証明書に含まれているサーバーの公開鍵を使って、共通鍵の元を暗号化します。
暗号化されたデータはサーバーに送信されますが、通信途中で盗聴されても中身を復号できるのはサーバーだけです。
なぜなら、この公開鍵に対応する秘密鍵を持っているのはサーバーだけだからです。
ステップ⑥ サーバーが秘密鍵で復号する

サーバーは、クライアントから受け取った暗号化されたデータを、自分だけが持っている秘密鍵を使って復号します。
これにより、サーバーはクライアントが生成した共通鍵の元を正しく取得できます。
結果として、
- クライアント
- サーバー
の両方が同じ共通鍵の元を共有することになります。
この共通鍵の元から生成された共通鍵が、以降のHTTPS通信で使用されます。
ステップ⑦ 暗号化された通信が始まる
共通の鍵が決まると、
ここから先はすべて暗号化された通信になります。
- HTTP リクエスト
- HTTP レスポンス
- ヘッダやボディの内容
これらは、ネットワーク上では中身の分からないデータとして流れます。
これでSSL/TLSの機能の1つである暗号化が実現できますね。
通信の改ざん防止はどうやって行うか?
HTTPS(TLS)では、通信内容の盗聴だけでなく改ざんも防ぐ仕組みが用意されています。
ここで言う改ざんとは、例えば次のようなケースです。
- 通信途中でレスポンス内容を書き換えられる
- リクエスト内容やパラメータがこっそり変更される
- ダウンロードしたデータに不正な内容を混ぜられる
TLSは、こうした改ざんを「必ず検出できる」ように設計されています。
結論から:改ざんは「MACの不一致」で検出する
TLSでは、通信データごとに
「このデータが改ざんされていないことを証明する値」
を計算し、データと一緒に送信します。
この値をMAC(メッセージ認証コード: Message Authentication Code) と呼びます。
MACは、
- 送信するデータ
- 送信者と受信者だけが知っている共通鍵
の両方を使って計算されます。
送信側で何が行われているか
暗号化通信が始まった後、
送信側(クライアント or サーバー)は次の処理を行います。
- 送信するデータを用意する
- データと共通鍵を使ってMACを計算する
- データとMACをセットで送信する
MACの計算には、内部的にハッシュ関数が使われています。
代表的な方式がHMAC(Hash-based MAC) です。
HMACでは、
- データと共通鍵を特定の方法で組み合わせる
- その結果をハッシュ関数(SHA-256など)で計算する
という処理が行われます。
重要なのは、共通鍵を知らない第三者は、正しいMACを作れないという点です。

受信側で何が行われているか
受信側では、次のように確認します。
- データとMACを受信する
- 受け取ったデータと共通鍵を使って、自分でもMACを計算する
- 計算したMACと、送られてきたMACを比較する
- 両者が一致すればOK、異なれば改ざんされている
ここで重要なのは、データが1文字でも変わるとMACは必ず変わるという点です。
これはMACの計算に使われるハッシュ関数の性質によるものです。
そのため、改ざんは確実に検出されます。
これでSSL/TLSの機能の1つである改ざんの防止が実現できますね。

Q&Aのコーナー
本物のサーバー証明書を偽サーバーに設置すれば偽装できる?
HTTPSで通信する際、クライアント(ブラウザなど)は通信先のサーバーからサーバー証明書を受け取ります。
そのため、本物のサーバー証明書を簡単に入手できます。
では手に入れた本物のサーバー証明書を偽サーバーに設置したら本物のサーバーになりすますことはできるのでしょうか?
結論としてはできません。
理由は偽サーバーは本物サーバーの秘密鍵を持っていないためです。
TLSハンドシェイクにて解説した「ステップ⑤ サーバーの公開鍵で暗号化して送る」を思い出してください。
このステップでは、クライアントは証明書に含まれているサーバーの公開鍵を使って、共通鍵の元を暗号化し、サーバーへ送信しました。
しかし、偽サーバーは本物のサーバーの秘密鍵を持っていないため、この共通鍵の元を復号することができません。
その結果、共通鍵を正しく生成できず、TLSハンドシェイクは失敗します。
よって、HTTPS通信は成立しません。
正しいURLを入力しても、偽サーバーにつながることはあるの?
あります。
正しいURL(例:https://example.com)を入力しても、
通信の途中で次のようなことが起きる可能性はゼロではありません。
- DNSが改ざんされ、別のIP アドレスに誘導される
- 公共Wi-Fiなどで通信を横取りされる
- ネットワーク途中に攻撃者が入り込む(中間者攻撃)
この場合、見た目上は「正しいURLにアクセスしている」ように見えても、
実際につながっているのは偽サーバーという可能性があります。
HTTPS(TLS)では、通信開始時に
「このサーバーは本当に example.com か?」
をサーバー証明書によって検証するのでブラウザは警告を出してくれます。
証明書エラーを無視して接続すると何が危険?
証明書エラーが出たとき、
「HTTPSだし暗号化はされてるでしょ?」
と考えて、エラーを無視して接続してしまうかもしれません。
確かに証明書エラーを無視しても、TLSによる暗号化そのものは行われます。
しかし問題は、その暗号化が誰との間で行われているかです。
証明書エラーを無視すると、「通信相手が本物である」という保証が失われます。
この状態では、中間者攻撃(MITM: Man-In-The-Middle Attack) が成立する可能性があります。
例えば、
- 利用者とサーバーの間に攻撃者が入り込む
- 攻撃者が自分の証明書を使ってHTTPS通信を行う
- 利用者はそれに気づかず通信を続けてしまう
という状況です。
結果として、
- ログイン情報を盗まれる
- 送信したデータを読まれる
- レスポンス内容を書き換えられる
といった被害につながる可能性があります。
HTTPSのサイトなら安全ですか?
HTTPSだからといって、安心できるとは限りません。
HTTPS(SSL/TLS)が保証しているのは、次の点です。
- 通信内容が暗号化されている
- 通信が改ざんされていない
- 接続先が「証明書に書かれたドメイン」の持ち主である
つまりHTTPSは、通信の安全性と、相手の身元確認を行う仕組みです。
よくある誤解に、
HTTPS = 正規の安全なサイト
というものがあります。
しかし実際には、フィッシングサイトであってもサーバー証明書は取得できます。
なぜなら、認証局(CA)が確認しているのは、「このドメインを本当に操作できるか」という点だけだからです。(一般的に使われているDV 証明書の場合)
例えば、
paypa1.examplelogin-example.comexample-secure-login.com
といった正規サイトに似せたドメインを正規に取得すれば、
そのドメイン用のサーバー証明書は問題なく発行されます。
その結果、
- URL は
https://... - 鍵マークも表示される
- 通信は暗号化されている
という、見た目上は「安全そう」な状態になります。
最後に
HTTPSは「暗号化されているから安全」というだけの仕組みではありません。
通信内容を守るだけでなく、「今つながっている相手が本当にそのサーバーなのか」を確認するところまで含めてSSL/TLSです。
サーバー証明書は、通信を暗号化するためのものというより、そのサーバーが本物だと第三者(認証局)が保証するためのものでした。
だからこそ、証明書をコピーしただけでは偽装できず、秘密鍵を持っていなければHTTPSの通信は成立しません。
自己署名証明書で警告が出るのも、「危険だから即アウト」という話ではなく、誰が保証しているのか分からないという状態をブラウザが正直に教えてくれているだけです。
仕組みが分かってくると、証明書エラーやHTTPSの警告に出会ったときも、「どこが信用できていないんだろう?」と一段落ち着いて見られるようになります。
Discussion
サーバ証明書でのなりすまし対策のことに重点が置かれている説明は非常によいと思います。
ただ少し問題はあると思います。
ハッシュ値を秘密鍵で暗号化 → 署名を公開鍵で復号してハッシュ値を取り出す、としていますが、暗号化と署名の混同です。
例えばこの記事で、zenn.dev の証明書を例に挙げられていますが、この証明書の検証の際に「ハッシュ値を取り出す」ということはしていません。
"実際の TLS 1.2 の主流や TLS 1.3 では、(略)" と補足したうえでRSA鍵交換を説明されているのは特に問題ないと思いますが、ではRSA鍵交換でない場合、証明書に含まれるサーバの公開鍵、サーバの持っている秘密鍵、これがどういう役割を持つのか。こちらについて記載が無いように思います。
そうした場合、証明書の存在意義が宙に浮いてしまうことになるので、細かい点まで触れないにしても、なんらか補足が必要だろうと思います。
※「暗号化には使わないが、公開鍵を使って秘密鍵の所有を確かめる手続きに使っている」くらいでも取り敢えずはよいのではないかと思います。