SSL/TLS とは — HTTPS 暗号化の仕組みと証明書 のサムネイル

SSL/TLS とは — HTTPS 暗号化の仕組みと証明書

⏱ 約 18 分 view 202 like 0 LOG_DATE:2026-05-09
目次 / TOC

TLS (Transport Layer Security) は、TCP の上に挟まる薄い暗号通信層。HTTPS の "S"、つまり URL の https:// や鍵マークの正体はこれ。アプリケーションプロトコル (HTTP / SMTP / IMAP ...) から見れば「ただの暗号化されたバイトストリーム」として透過的に動くので、HTTP を一切改造せずに HTTPS が実現できた。本稿は 役割 → ハンドシェイク → 証明書と PKI → 暗号アルゴリズム → openssl での診断 → 過去の脆弱性 → 運用の現実解 という流れで、サーバ運用とトラブルシュートに必要な範囲だけを扱う。

▸ ネットワーク初学者へ — まずこの 3 つだけ

難しく見えても、本質は次の 3 つだけ持ち帰ればいい。(1) TLS は ブラウザの鍵マーク 🔒 の中身で、HTTPS の "S" がこれ。(2) 仕事は 「盗聴されない / 改ざんされない / 相手が本物だと分かる」の 3 つ。(3) 通信を始める前に「ハンドシェイク」と呼ばれる挨拶で 合言葉 (= 鍵) を決め、相手の身分証 (= 証明書) を確認してから本文を送る。— ここを土台に各章を順に開いていけば、迷子になりにくい。

01

TLS が提供する 3 つの保証 #

TLS が提供するのは、シンプルに次の 3 つだけ

  • 機密性 (Confidentiality) — 通信内容を盗聴されても読めない (AES-GCM / ChaCha20-Poly1305)
  • 完全性 (Integrity) — 通信内容が改ざんされたら検出できる (AEAD の認証タグ)
  • 認証 (Authentication) — 通信相手が「本物」であることを保証 (X.509 証明書 + PKI)

3 つ目の 認証 が一番大事で、これがなければ前 2 つは無意味。攻撃者が中間者になっていれば「攻撃者と暗号化通信して終わり」だからだ。curl -k--insecure はこの保証を捨てる操作で、本番では絶対に使わない。

▸ かみ砕いて言うと — TLS は「封筒 + 封印 + 身分証」

普通のはがき (HTTP) は配達員にも近所の人にも読まれるし、書き換えても誰にもバレない。TLS はこれを (1) 封筒に入れて中を読めなくし (機密性)、(2) 封印シールで開封の有無が分かるようにし (完全性)、(3) 差出人が名乗っている本人だと身分証で確認する (認証) — の 3 段構えで守る。curl -k は「相手の身分証を見ずに荷物を渡す」操作、つまり 暗号化はしても相手が偽物かもしれない状態。本番では絶対に使わない。

▸ 「SSL」と「TLS」の使い分け

歴史的経緯で「SSL」と「TLS」が混在して呼ばれるが、現代運用されているのはすべて TLS。Netscape の SSL 2.0 (1995) / 3.0 (1996) を IETF が標準化して TLS 1.0 (1999) → 1.1 (2006) → 1.2 (2008) → 1.3 (2018, RFC 8446) と進化、2026 年現在の HTTPS は 95% 以上が TLS 1.2 か 1.3。それでも「SSL 証明書」「SSL 化」といった慣用表現は商業文書に残るので、両方に反応できればよい。

TLS が挟まる位置は Application 層と Transport 層の間。HTTP は TCP の上に乗るが、HTTPS は HTTP → TLS → TCP の 3 層構造になり、TLS が暗号化・復号して上下の層に透過させる。HTTP/3 では TLS 1.3 が QUIC (UDP の上の独自トランスポート) に組み込まれている。

02

ハンドシェイク — 1 往復で鍵を作る #

ハンドシェイクは TLS の心臓部で、鍵交換と相手認証を完了させ、以降の通信に使う セッション鍵 を確立する。TLS 1.3 では 1-RTT (1 往復) で完了し、TLS 1.2 の半分。

▸ かみ砕いて言うと — ハンドシェイクは「会話前のあいさつ」

外国の人と話し始める前に 「どの言語で話すか」「合言葉は何か」「相手は本物か」を一気に決めるのがハンドシェイク。ClientHello が「英語と日本語どっちで話せます?」、ServerHello が「英語で。あと身分証はこれです」、Finished が「OK、じゃあ本題に入りましょう」。TLS 1.3 ではこのあいさつが 1 往復 (1-RTT) で終わるので、ページ表示が体感で速くなった、というのが実用上のメリット。

1. ClientHello (Client → Server)
対応 TLS バージョン / 暗号スイート / key_share (ECDHE 公開鍵) / 署名アルゴリズム / SNI (繋ぎたいホスト名) を一気に提示。
2. ServerHello + 証明書 (Server → Client)
ServerHello で鍵共有が完了 → 直後から暗号化開始。サーバ証明書・CertificateVerify (秘密鍵による署名)・Finished を暗号化済みで送る (TLS 1.2 までは証明書が平文だった)。
3. Finished (Client → Server)
クライアントが証明書チェーンを検証 → ハンドシェイク完了の MAC を返答。これ以降は application_data が対称暗号で流れるだけ。

実際に 1 ステップずつ動かす — ClientHello から Finished まで、メッセージがクライアント⇄サーバ間をどう往復して暗号化が確立するかを追ってみよう。

ポイントは「ClientHello に key_share を載せて出す」こと。クライアントが見切り発車で ECDHE の公開鍵を投げるので、サーバの 1 往復目で鍵共有が成立する。TLS 1.2 は ServerHello を待ってから ClientKeyExchange を投げる 2-RTT 構造だった。

TLS 1.2 (2-RTT) と TLS 1.3 (1-RTT) の差
# TLS 1.2 — 証明書は平文、2 往復 Client Server | ClientHello | |------------------------------------>| | ServerHello / Certificate / KeyEx | ← 平文で流れる |<------------------------------------| | ClientKeyExchange / Finished | |------------------------------------>| | Finished | |<------------------------------------| |===== Application Data (暗号化) =====| # TLS 1.3 — 証明書も暗号化、1 往復 Client Server | ClientHello (+ key_share) | |------------------------------------>| | ServerHello + {Cert, Verify, Fin} | ← {} は暗号化済 |<------------------------------------| | Finished | |------------------------------------>| |===== Application Data (暗号化) =====|
▸ 0-RTT (Early Data) はリプレイ可能

過去にハンドシェイク済みのサーバなら、再接続時に PSK (Pre-Shared Key) を使って 0-RTT で application_data を最初の往復に乗せることができる。スマホアプリで毎回ハンドシェイクの遅延を払いたくないユースケースで効く。ただし 0-RTT データはリプレイ可能性があるため、副作用のない idempotent な GET 等に限定する。決済処理を 0-RTT に乗せてはいけない。

03

証明書と PKI — 信頼の連鎖 #

▸ かみ砕いて言うと — 証明書は「ドメインのパスポート」

海外の入国審査で見せるのがパスポート、Web サイトが「自分は本物の hackinglabo.com だ」と示すのが サーバ証明書。パスポートを発行する政府が CA (認証局)、その大元の信頼の根が Root CA、実発行を担当する出先機関が Intermediate CA。ブラウザは「サーバ証明書 → 中間 CA → Root CA」と辿り、最後の Root が OS / ブラウザに最初から登録されている信頼リストに入っていれば「本物」と判断する。Let's Encrypt はこのパスポートを 無料で機械的に発行してくれる仕組みで、これのおかげで個人サイトでも HTTPS 化が当たり前になった。

サーバ証明書は X.509 形式で、次のような情報の塊。

  • SubjectCN=hackinglabo.comSAN (Subject Alternative Name) に対象ドメインを列挙
  • Issuer — どの CA が署名したか
  • Validity — 有効期間 (パブリック CA は最大 398 日)
  • Public Key — このドメインの公開鍵 (RSA / ECDSA / Ed25519)
  • Extensions — Key Usage / EKU / AIA / CRL DP / SCT (Certificate Transparency)
  • Signature — 上記全フィールドに対する CA の署名

実サーバ証明書は単独で立たず、CA の署名で 「Root CA → Intermediate CA → Server Cert」 という証明書チェーンの末端にある。OS / ブラウザのトラストストアに登録された Root CA まで遡れて初めて「信頼」が成立する。

証明書チェーンの典型 (Let's Encrypt の場合)
Root CA ISRG Root X1 # OS/ブラウザにバンドル、自己署名 │ 署名 Intermediate R3 / R10 / E5 # 実発行はこの中間 CA から │ 署名 Server Cert CN=hackinglabo.com # 配信される証明書 # クライアントは Server → Intermediate → Root と辿り、Root が信頼済なら OK

Let's Encrypt と ACME による自動化 #

2015 年開始の Let's Encrypt は無料・自動化で証明書を発行する CA で、現在世界の HTTPS の 半分以上 を占める。プロトコルは ACME (RFC 8555)certbot / acme.sh などがドメイン所有確認 (HTTP-01 / DNS-01 / TLS-ALPN-01) を経て 90 日有効 の証明書を自動で取得・更新する。

チャレンジ 仕組み 向くケース
HTTP-01 http://example.com/.well-known/acme-challenge/... にトークンを置く 一般的な Web サーバ
DNS-01 _acme-challenge.example.com の TXT レコードにトークンを書く ワイルドカード証明書、内部サーバ
TLS-ALPN-01 443 番で特殊な ALPN を返す HTTP-01 が使えない 443 のみのサービス

失効確認と Certificate Transparency #

鍵漏洩などで証明書を強制的に無効化する仕組みが失効。現代の主流は、サーバが OCSP 応答を取得してハンドシェイクに同梱する OCSP Stapling と、ブラウザがブルームフィルタで一括配布する CRLite / OneCRL の併用。

不正発行検出のため、CA は発行した全証明書を 公開ログ (Certificate Transparency) に記録することが Chrome から義務化されている (2018 年〜)。証明書には少なくとも 2 つの独立 CT ログ由来の SCT が埋め込まれる。運用者は crt.sh で「自ドメインに対していつ・どこの CA がどんな証明書を出したか」を全部監視できる — 不正発行の早期発見の決め手。

04

暗号アルゴリズム — Forward Secrecy と AEAD #

TLS 1.2 の暗号スイートは「鍵交換 + 認証 + 対称暗号 + MAC」の 4 要素を 1 文字列で指定する複雑な仕組みだった。TLS 1.3 でこれが大幅に整理され、現代運用で出会う組み合わせは数えるほど。

▸ かみ砕いて言うと — 暗号スイートは「セットメニュー」

鍵交換 + 認証 + 対称暗号 + ハッシュ の 4 部品の組み合わせ表が暗号スイート。ラーメン屋でいう「醤油 + 細麺 + 中盛 + 味玉」のセット指定。TLS 1.2 までは選択肢が膨大で運用者が選定ミスをしがちだった反省から、TLS 1.3 では 「弱い具材」を全部メニューから消した。だから TLS 1.3 を有効にして後述の Mozilla Intermediate プロファイルを貼っておけば、暗号スイートの細かい中身は意識しなくてよい設計になっている。Forward Secrecy は「サーバの長期秘密鍵が 将来漏れても、過去に流れた通信ログは復号できない」性質 — 言い換えると 「過去の自分」を未来の事故から守る保険

暗号スイートの読み方
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 # TLS 1.2 形式 │ │ │ │ │ │ │ └─ HKDF / PRF のハッシュ │ │ └─ 対称暗号 (AEAD) │ └─ サーバ証明書の鍵タイプ (認証) └─ 鍵交換 (ECDHE = Forward Secrecy あり) # TLS 1.3 — 鍵交換と認証は別交渉、対称暗号 + ハッシュだけになる TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256

鍵交換 — Forward Secrecy が分水嶺 #

Forward Secrecy (FS, PFS) とは「長期の秘密鍵が将来漏洩しても、過去の通信は復号できない」性質。これを満たすのは一時鍵を使う DHE / ECDHE だけで、Premaster を RSA で暗号化送信する古典的な「RSA 鍵交換」では満たせない (RSA 秘密鍵が将来漏れたら過去の全通信が復号できる)。TLS 1.3 で RSA 鍵交換が完全削除されたのはこのため。実運用の鍵交換は X25519 か P-256 の ECDHE がほぼすべて。

AEAD で平文と認証を一体化する #

AEAD (Authenticated Encryption with Associated Data) は暗号化と完全性検証を 1 操作で行う方式。「先に MAC かけてから暗号化するか、暗号化してから MAC かけるか」といった padding oracle 攻撃の温床になる順序問題を構造的に消す。TLS 1.3 では AEAD のみが許され、選択肢は実質次の 3 種類。

  • AES-128-GCM / AES-256-GCM — CPU の AES-NI で爆速、サーバ向け定番
  • ChaCha20-Poly1305 — AES-NI のないモバイル CPU で高速、Google 主導
  • AES-CCM — IoT 向けの軽量版

サーバ証明書の署名鍵 #

「サーバ本物」を証明するための鍵タイプは 3 系統。古い順に RSA-2048 / 3072 (互換性最強)、ECDSA P-256 / P-384 (鍵が短く高速)、Ed25519 (TLS 1.3 から標準化、最新)。一般 Web サイトは ECDSA P-256 + RSA-2048 のデュアル証明書 が無難。

▸ TLS 1.3 で削除された「弱いものたち」
  • RSA 鍵交換 — Forward Secrecy なし
  • CBC モード — BEAST / Lucky13 / POODLE の温床
  • 3DES / RC4 — 古典的に弱い対称暗号
  • MD5 / SHA-1 — 衝突耐性なし
  • 圧縮 — CRIME / BREACH の温床

これらをまとめて切ったので、TLS 1.3 サーバを建てるだけで弱い暗号は構造的に選ばれない。

05

openssl で TLS を診断する #

TLS が絡む不具合のほぼすべて — 証明書チェーン不備、SNI ミス、古いプロトコルへの fallback、期限切れ — は openssl 1 本で原因が割り出せる。

▸ はじめての人向け — まず叩く 1 本

たくさんあるオプションに圧倒される前に、初手はこれだけでよい:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates
これで 「誰のサイトか / どの CA が発行したか / 有効期限はいつまでか」の 3 点が一発で見える。証明書まわりの調査の 9 割はこの 1 本で片付く。深掘りしたくなったら、本章後半の testssl.sh や Qualys SSL Labs に進めばいい。

openssl s_client — ハンドシェイクを目視する
# 接続して証明書チェーン + ハンドシェイクを観察 $ openssl s_client -connect hackinglabo.com:443 -servername hackinglabo.com # プロトコルバージョン指定 (互換性テスト) $ openssl s_client -connect example.com:443 -tls1_3 $ openssl s_client -connect example.com:443 -tls1_2 # 証明書を全部 PEM で取り出す (チェーン検証に使う) $ openssl s_client -connect example.com:443 -showcerts < /dev/null
openssl x509 — 証明書の中身を読む
# サーバから取って直接表示 (期限・SAN・発行元の確認に最も使う) $ echo | openssl s_client -connect example.com:443 2>/dev/null \ | openssl x509 -noout -subject -issuer -dates -ext subjectAltName # 証明書ファイルから読む $ openssl x509 -in cert.pem -noout -text # SHA-256 フィンガープリント (HPKP / DANE で使う) $ openssl x509 -in cert.pem -noout -fingerprint -sha256
自己署名証明書を作る (開発・テスト用)
# ECDSA P-256 鍵を生成 $ openssl ecparam -genkey -name prime256v1 -out key.pem # 自己署名証明書 (1 年有効) を 1 行で $ openssl req -x509 -key key.pem -out cert.pem -days 365 \ -subj "/CN=test.example.com"
curl / nmap / testssl.sh — 一段上の自動診断
# curl — TLS ハンドシェイクログを詳細に $ curl -v --tls-max 1.3 https://example.com # nmap — 対応暗号スイート列挙、Heartbleed チェック $ nmap --script ssl-enum-ciphers -p 443 hackinglabo.com $ nmap --script ssl-heartbleed -p 443 example.com # testssl.sh — 設定ミス・既知脆弱性を 1 発で全部洗う定番ツール $ ./testssl.sh https://hackinglabo.com $ ./testssl.sh -U https://hackinglabo.com # 脆弱性のみ

オンライン版なら Qualys SSL Labs が定番で、A+ / A / B のグレード評価がもらえる。本番公開前に 1 回は通したい。

06

過去の脆弱性が教えてくれること #

TLS の過去の脆弱性史は「現代の設定で何を捨てるべきか」をそのまま示している。代表だけ押さえれば、運用設定の意味が腑に落ちる。

▸ かみ砕いて言うと — 脆弱性史は「捨てるべきものリスト」

過去の TLS 脆弱性は要するに 「過去にこんな失敗があったから、現代は これを使ってはいけない」という生きた教訓集。BEAST / POODLE → CBC は捨てるCRIME → TLS 圧縮を切るFREAK / Logjam → 短い RSA / DH を切るHeartbleed → OpenSSL は常に最新に保つ。下の表を一つひとつ暗記する必要はなく、TLS 1.3 + Mozilla Intermediate プロファイルを選ぶだけで、これらの「危ない選択肢」は構造的に消える、という事実だけ最初に押さえておけばよい。

攻撃名 何が起きた 教訓
2011 BEAST TLS 1.0 / CBC で平文を 1 byte ずつ復元 CBC は捨てる、AEAD に移行
2012 CRIME / BREACH TLS / HTTP 圧縮を悪用して Cookie をリーク TLS 圧縮は無効化
2013 Lucky 13 CBC + HMAC の処理時間差で padding 推測 AEAD への完全移行
2014 Heartbleed OpenSSL の Heartbeat 拡張のバウンドチェック忘れで、サーバメモリ最大 64 KB がリーク。秘密鍵まで漏れうる (CVE-2014-0160) 全証明書再発行 + パッチ運用
2014 POODLE SSL 3.0 の CBC パディング検証の甘さで平文復元 SSL 3.0 完全無効化 (RFC 7568)
2015 FREAK / Logjam 90 年代の輸出規制で残った 512 bit RSA / DH をダウングレード悪用 EXPORT 暗号削除、DH を 2048 bit 以上
2016 DROWN 同じ秘密鍵を SSL 2.0 と TLS で共用 → 復号 SSL 2.0 を世界中から排除
2017 ROBOT 1998 年の RSA Bleichenbacher 攻撃の再来 ECDHE / TLS 1.3 移行
▸ Heartbleed が突きつけた真実

OpenSSL の Heartbeat 拡張の境界チェック忘れというたった 1 つのバグで、世界中のサーバの秘密鍵が漏洩しうる状態になった。Yahoo!・Google・AWS が同時に対応した過去最悪級の TLS 脆弱性で、「暗号は数学だが、実装は人間が書く」ことを思い出させる事件。openssl version を時々確認し、CVE が出たら即パッチ、というオペレーションが現代運用の基本になった。

MITM (中間者攻撃) と現代の防御 #

不正な CA 発行 (DigiNotar 2011 / Symantec 2017) や、企業・家庭ルータの「TLS インスペクション」での証明書差し替えなど、中間者は今も現実の脅威。対策は次の組み合わせ。

  • HSTS — 一度 HTTPS で接続したサイトは平文 HTTP を拒否、hstspreload.org でブラウザに事前登録
  • Certificate Transparency — 不正発行を CT ログで可視化、crt.sh で監視
  • DANE / TLSA — DNSSEC で証明書を縛る (運用ハードルは高い)
  • CRLite / OneCRL — ブラウザ側で失効を一括配布
07

運用の現実解 #

ここまでの内容を踏まえると、実運用で押さえるべきは「プロファイル選び」と「勝手に古びさせない仕組み」の 2 点に集約される。

▸ はじめての人向け — 個人サイトはこの 4 点だけ揃えれば十分

個人ブログや小規模 Web サイトで TLS をきちんと整えたいなら、まずこの 4 つを揃えればよい。(1) Let's Encrypt + certbot で証明書を取得。(2) Mozilla の Intermediate プロファイルを nginx / Apache 設定にそのまま貼る。(3) HTTP → HTTPS の 301 リダイレクト + HSTS で平文経路を構造的に閉じる。(4) cron で証明書自動更新。仕上げに「年に 1 回、Qualys SSL Labs で A+ が出ているか」を確認する習慣だけ付けておけば、運用として十分に堅い。

Mozilla のテンプレートを使う #

ハンドシェイクと暗号スイートの組み合わせを自前で考える必要はない。Mozilla SSL Configuration Generator (ssl-config.mozilla.org) が 3 プロファイルをコピペで提供する。これが事実上の業界標準。

プロファイル 対応 TLS 想定読者
Modern 1.3 のみ API / 社内系で「最新ブラウザだけ」で済むとき
Intermediate 1.2 + 1.3 一般 Web サイト。迷ったらこれ
Old 1.0〜1.3 Windows XP / Android 4 への互換が法的に必要なときのみ

仕組みで守る運用ループ #

設定を 1 度入れたら終わりにせず、自動更新と定期監視で「気付ける」状態を作る。事故の大半は人の手動オペレーションを残したことに起因する。

  • certbot / acme.sh + cron で証明書を自動更新 — 手動更新を残すと期限切れ事故は必ずいつか起こる
  • OCSP Stapling を有効化 — 失効確認の遅延とプライバシー漏洩を同時に解消
  • HTTP → HTTPS 301 + HSTS preload 登録 (hstspreload.org) — 平文 HTTP を構造的に閉じる
  • testssl.sh / Qualys SSL Labs を定期実行 — 年 2 回 + 重大 CVE 公表時に走らせる
  • crt.sh で CT 監視 — 自ドメインへの不正発行を早期発見
  • OpenSSL を定期更新 — Heartbleed 級の CVE は今後も出る前提でパッチ運用
▸ 急がなくていい新機能

SNI を暗号化する ECH (Encrypted Client Hello) や、量子コンピュータ耐性のある PQ 暗号 (X25519MLKEM768 など) は 2026 年時点でブラウザと CDN で段階導入が始まったところ。情報を追っておく価値はあるが、本番投入を急ぐより、まずは TLS 1.3 + Intermediate プロファイル + 自動更新の土台を盤石にする方が利得が大きい。

𝕏 ポスト B! はてブ