HTTPS 與 TLS 的真正運作方式:握手流程詳解
你在 URL 前面輸入了 https://,看到鎖頭圖示,然後就繼續做別的事了。但當憑證在凌晨兩點過期、混合內容警告讓你的結帳頁面無法運作,或是客戶問你為什麼他們的 API 呼叫會出現 TLS 錯誤時,「鎖頭代表安全」就不再是個有用的答案。本文將說明 HTTPS 連線在網路上實際發生了什麼事,以及如何不靠猜測來配置它。
HTTPS 為 HTTP 增加了什麼
HTTPS 就是封裝在 TLS(Transport Layer Security,傳輸層安全性)工作階段中的一般 HTTP。TLS 提供三項具體保證:
- 機密性 — 流量經過加密,中間人無法讀取。
- 完整性 — 傳輸過程中的竄改會被偵測到,連線會失敗。
- 身分驗證 — 伺服器證明它擁有其所出示憑證對應的私鑰。
請注意清單上沒有的東西:HTTPS 不保證伺服器是否值得信任、應用程式是否有漏洞,或資料在靜態儲存時是否安全。它保護的是通道,不是端點。
TLS 握手,一步一步來
握手是在任何 HTTP 位元組流動之前進行的協商。在 TLS 1.3 中只需要一個往返;TLS 1.2 需要兩個。以下是 TLS 1.3 流程的簡單說明:
- ClientHello — 客戶端傳送支援的 TLS 版本、加密套件清單、隨機 nonce,以及用於 Diffie-Hellman 金鑰交換的金鑰份額(通常是 X25519)。
- ServerHello — 伺服器選擇版本與加密套件,回傳自己的金鑰份額,並傳送憑證鏈以及對握手 transcript 的簽章。
- 金鑰衍生 — 雙方從 Diffie-Hellman 交換計算出共享密鑰。臨時金鑰意味著工作階段金鑰是唯一的,且具備前向保密性:日後伺服器的長期金鑰若遭洩露,也無法解密已記錄的流量。
- Finished — 雙方對 transcript 傳送 MAC。若有任何內容被竄改,握手就會中止。
- 應用程式資料 — HTTP 請求和回應使用從共享密鑰衍生的對稱金鑰加密。
憑證是身分驗證的核心。伺服器用其私鑰簽署握手;客戶端使用憑證中的公鑰驗證該簽章,然後對照受信任的根 CA 驗證憑證本身,檢查主機名稱、有效日期和撤銷狀態。
對稱與非對稱:為何兩者並用
非對稱加密(RSA 或 ECDSA)速度慢,但解決了金鑰分發問題。對稱加密(AES-GCM、ChaCha20-Poly1305)速度快,但需要共享金鑰。TLS 僅在握手期間使用非對稱加密來建立對稱工作階段金鑰,然後用該金鑰加密大量資料。這就是為什麼現代 TLS 1.3 連線很快:一個往返,然後其餘一切使用成本低廉的 AES 或 ChaCha20。
加密套件與真正重要的事
加密套件是一組具名的演算法組合。在 TLS 1.3 中清單很短,所有選項都被認為安全;在 TLS 1.2 中你必須謹慎選擇。快速比較:
| 面向 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 握手往返次數 | 2(或使用恢復時為 1) | 1(使用恢復時可 0-RTT) |
| 金鑰交換 | RSA 或 (EC)DHE | 僅限臨時 (EC)DHE |
| 前向保密 | 選用 | 一律具備 |
| 舊式加密(CBC、RC4) | 配置錯誤時可能出現 | 已移除 |
實務要點:僅啟用 TLS 1.2 和 1.3,停用 TLS 1.0/1.1,並優先使用 ECDSA 憑證以降低握手成本。讓你的伺服器或反向代理來決定套件順序;在 2024 年代的軟體中,大多數預設值都是合理的。
如何檢查真實連線
你不需要信任文件——驗證你自己的端點。openssl s_client 會顯示協商出的版本、加密方式和憑證鏈:
openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null
查看 Protocol 和 Cipher 行,然後檢查憑證鏈輸出中的到期日和 SAN 項目。如果鏈不完整,某些客戶端會失敗,而瀏覽器卻能成功,這是「在 Chrome 中可行但在 curl 中不行」這類錯誤的經典來源。
常見的 HTTPS 配置錯誤
- 缺少中間憑證 — 伺服器只傳送其葉憑證。瀏覽器可能會自行取得中間憑證,但 API 客戶端通常不會。
- 混合內容 — HTTPS 頁面透過 HTTP 載入指令碼或圖片;瀏覽器會封鎖或降級它。用瀏覽器主控台進行稽核。
- 沒有 HSTS — 沒有
Strict-Transport-Security,第一個 HTTP 請求可能在重新導向發生前就被攔截。 - 薄弱或過時的協定版本 — 為了舊客戶端而保留 TLS 1.0 啟用,會悄悄削弱每個連線。
- 憑證過期 — 自動化續約;手動的行事曆提醒不是策略。
合理的伺服器配置
對於 Nginx,一個最小的現代區塊看起來像這樣:
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
fullchain.pem 檔案就是防止缺少中間憑證問題的關鍵。將從 80 埠的重新導向放在單獨的 server 區塊中,這樣 HTTP 仍可用於 ACME 挑戰。
HTTPS 不保護什麼
一旦請求到達你的伺服器,TLS 的任務就結束了。注入漏洞、存取控制缺失、日誌中洩露的機密,以及配置錯誤的 CORS,全都存在於傳輸層之上。把 TLS 視為基準,而非安全計畫。如果你正在調查事件,請及早將傳輸問題(握手失敗、憑證過期)與應用程式問題(500 錯誤、錯誤的酬載)分開——它們的修復方法完全不同。
常見問題
HTTPS 會加密 URL 路徑和查詢字串嗎?
會。在 HTTPS 中,整個 HTTP 請求,包括路徑和查詢字串,都位於加密的 TLS 記錄內。然而,除非使用 Encrypted Client Hello,否則主機名稱(SNI)和目的 IP 在握手期間是可見的。
TLS 1.3 一定比 TLS 1.2 快嗎?
通常是的,因為它節省了一個往返並支援 0-RTT 恢復。在高延遲連線上差異很明顯;在區域網路上則微不足道。更大的優勢在於 TLS 1.3 完全移除了舊式加密選項。
如果我將 HTTP 重新導向到 HTTPS,還需要 HSTS 嗎?
需要。重新導向只有在瀏覽器已到達你的伺服器後才有幫助。HSTS 告訴瀏覽器對未來的請求完全跳過 HTTP,關閉第一個請求可能被攔截的視窗。
當你稽核部署並需要快速檢查憑證詳細資訊或解碼擷取到的握手時,請保持終端機工作流程順手——如果你要為團隊記錄發現,Markdown 編輯器是將原始 openssl 輸出轉換成可讀報告的快速方法。