HTTPS 與 TLS 實戰指南:深入理解運作原理
你已經看過瀏覽器中的鎖頭圖示,也知道 HTTPS 很重要。但你真的了解瀏覽器連線到 HTTPS 網站時發生了什麼事嗎?許多開發者把 TLS 當成黑盒子——設定一次就忘了它。這在一般情況下可行,直到出了問題、效能變差,或是安全稽核發現錯誤配置。
本指南以實用角度解釋 HTTPS 和 TLS。你將了解握手過程、憑證如何建立信任,以及在伺服器上正確實作 TLS 的步驟。不會有過多的術語——只給你除錯和做出明智決策所需的概念。
HTTPS 究竟是什麼
HTTPS 是建立在 TLS(Transport Layer Security,傳輸層安全性)之上的 HTTP。它提供三項保證:
- 加密:用戶端與伺服器之間交換的資料對竊聽者來說無法讀取。
- 認證:用戶端驗證它正在與合法的伺服器通訊,而非冒充者。
- 完整性:資料在傳輸過程中無法被修改而不被偵測。
TLS 是實現這一切的協定。SSL 是它的前身——你仍然會在行銷文案中看到「SSL 憑證」,但現代系統使用 TLS(目前最新版本是 TLS 1.3)。
TLS 握手:逐步解析
在任何 HTTP 資料流動之前,用戶端和伺服器會執行握手,以議定加密參數並驗證身分。以下是 TLS 1.3(現代標準)的簡化流程:
- Client Hello:瀏覽器發送支援的加密套件清單、TLS 版本,以及一個隨機值(client random)。
- Server Hello:伺服器選擇一個加密套件,發送自己的隨機值(server random),並提供其憑證鏈。
- 金鑰交換:使用臨時性 Diffie-Hellman(或類似機制),雙方在不直接傳輸共享秘密的情況下推導出它。這提供了前向保密性——日後伺服器私鑰外洩也無法解密過去的會話。
- 憑證驗證:用戶端根據受信任的根 CA 檢查憑證、驗證網域名稱,並檢查到期和撤銷狀態。
- 完成:雙方發送 MAC(訊息驗證碼)以確認握手未被篡改。加密的 HTTP 流量開始傳輸。
TLS 1.3 將握手減少到一個往返(1-RTT),並支援 0-RTT 恢復以造福重複訪客,從而改善延遲。TLS 1.2 需要兩個往返。
憑證:信任的錨點
TLS 憑證將公開金鑰綁定到網域名稱,並由憑證授權單位(CA)簽署。當你的瀏覽器連線到 example.com 時,它會檢查憑證:
- 是否由受信任的 CA 簽署(或可鏈結到其中一個)。
- 是否與網域名稱相符(透過主體別名)。
- 是否在有效期限內。
- 是否未被撤銷(透過 OCSP 或 CRL,儘管執行方式各異)。
你可以從 Let's Encrypt 免費取得憑證,它透過 ACME 協定發行 90 天效期的憑證。Certbot 等自動化工具會處理續約。對於內部服務,你可以執行自己的 CA,但用戶端必須明確信任它。
在伺服器上配置 TLS
正確的配置需要在安全性、相容性和效能之間取得平衡。以下是一份檢查清單:
- 僅使用 TLS 1.2 和 1.3。停用 TLS 1.0 和 1.1——它們已被棄用且有漏洞。
- 選擇強健的加密套件。偏好 AEAD 加密,如 AES-GCM 或 ChaCha20-Poly1305。避免 CBC 模式和 RC4。
- 啟用前向保密性。使用 ECDHE 金鑰交換。
- 啟用 OCSP 裝訂。伺服器包含快取的 OCSP 回應,減少用戶端延遲和隱私外洩。
- 設定 HSTS 標頭。強制瀏覽器在未來造訪時使用 HTTPS。
- 將 HTTP 重新導向到 HTTPS。確保不提供任何明文流量。
對於 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_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8;
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
proxy_pass http://localhost:3000;
}
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
使用 SSL Labs 的 SSL Test 或 testssl.sh 等工具測試你的配置。以 A 級為目標,但如果你必須支援較舊的用戶端,不要為了追求滿分而犧牲相容性。
常見陷阱與避免方法
- 憑證過期:自動化續約並監控到期時間。單一過期憑證就可能讓你的網站下線。
- 混合內容:在 HTTPS 頁面載入 HTTP 資源會觸發警告並破壞功能。使用相對 URL 或全面採用 HTTPS。
- 弱協定:TLS 1.0/1.1 已被棄用。更新你的配置。
- 缺少中繼憑證:伺服器必須發送完整的鏈(不包括根憑證)。缺少中繼憑證會導致某些用戶端出現信任錯誤。
- 不正確的重新導向:在提供任何內容之前將 HTTP 重新導向到 HTTPS,並使用 301 進行永久重新導向。
常見問題
HTTPS 和 TLS 有什麼區別?
HTTPS 是執行在 TLS 之上的 HTTP。TLS 是提供加密、認證和完整性的密碼學協定。沒有 TLS 就沒有 HTTPS。
我需要為 www 和非 www 分別申請憑證嗎?
你可以將兩者都包含在單一憑證的主體別名(SAN)欄位中。大多數 CA 預設或透過簡單配置即可發行涵蓋兩者的憑證。
我應該多久輪換一次 TLS 憑證?
遵循 CA 的有效期限——Let's Encrypt 為 90 天。使用 cron 工作或 systemd 計時器自動化續約。如果私鑰外洩,應立即輪換。
準備好檢查你的網路流量了嗎?使用 Nginx 日誌分析器 來解析存取日誌,並驗證 HTTPS 請求、狀態碼和潛在問題。