Skip to content

SSL 憑證

以前,大家都使用 http 協定傳輸資料,但因為缺乏加密設計,導致資料在傳輸過程中很容易被竊聽和篡改。 為了解決這個問題,SSL(Secure Sockets Layer)被引入了。

SSL 是一種安全協定,最早是由 Netscape 公司開發的,後來 IETF 將 SSL 進行標準化並改名為 TLS(Transport Layer Security)。他使用了複雜的加密算法來保護資料的安全,確保資料在傳輸過程中不被竊聽和篡改。

但若單純使用 SSL/TLS 協定,仍然無法防禦中間人攻擊,因為攻擊者可以冒充伺服器來竊聽和篡改資料。

中間人攻擊:

flowchart LR
    A["客戶端"]
    M["攻擊者\n偽造雙向身份"]
    S["伺服器"]

    A<-. "假裝是伺服器\n(使用自己的憑證進行通訊)" .->M
    M<-. "假裝是客戶端\n(使用自己的憑證進行通訊)" .->S

    style A fill:#EBF3FB,stroke:#85B7EB,color:#0C447C
    style M fill:#FCEBEB,stroke:#E24B4A,color:#A32D2D
    style S fill:#E1F5EE,stroke:#5DCAA5,color:#085041

為了防中間人攻擊,CA(Certificate Authority)機構被引入了。

CA 是一個受信任的第三方機構,負責簽發和管理 SSL 憑證。簽發 SSL 憑證的過程中,CA 會驗證申請者的身份,確保他們是合法的網站所有者。

當客戶端連接到伺服器時,伺服器會提供 SSL 憑證給客戶端,客戶端會檢查憑證是否由受信任的 CA 簽發,以及憑證是否有效。如果憑證無效或未被信任,客戶端會顯示警告訊息,提醒使用者可能存在安全風險。

如下圖即是瀏覽器對於無效憑證的警告訊息:

SSL

憑證是由信任鏈(Chain of Trust)來確保其真實性的。

通常,一個完整的信任鏈包含三個主要的層級:

  1. 根憑證 (Root Certificate): 由 CA 機構自己簽發給自己,並且預先內建在我們的作業系統或瀏覽器的信任庫中。這代表我們的設備預設且無條件信任這些根 CA。
    如下圖中的 GTS Root R4

  2. 中介憑證 (Intermediate Certificate): 為了保護根憑證的私鑰安全 (如果根憑證外洩,後果不堪設想),根 CA 通常不會直接簽發最終的網站憑證,而是將權限授權給中介 CA。中介 CA 就像是橋樑,負責日常的憑證簽發工作。
    如下圖中的 WE1

  3. 伺服器憑證 (Server / Leaf Certificate): 最終簽發給網站的憑證。這也就是我們架設網站時需要安裝到伺服器上的那張憑證。
    如下圖中的 cpp.doong.me

CA
flowchart TD
    Root["根憑證 (GTS Root R4)\n預先安裝在作業系統或瀏覽器中"]
    Inter["中介憑證 (WE1)\n由根憑證簽署並授權"]
    Leaf["伺服器憑證 (cpp.doong.me)\n我的網站所使用的憑證"]

    Root --"信任並驗證"--> Inter
    Inter --"信任並驗證"--> Leaf

    style Root fill:#E1F5EE,stroke:#5DCAA5,color:#085041
    style Inter fill:#FFF9C4,stroke:#FBC02D,color:#F57F17
    style Leaf fill:#EBF3FB,stroke:#85B7EB,color:#0C447C

SSL 憑證有一個有效期線 (例如上面圖中的憑證會在 2026/8/7 到期),過了這個日期,憑證就會失效,瀏覽器就會顯示警告訊息,提醒使用者可能存在安全風險。

且最近的趨勢是縮短憑證的有效期,以提高安全性。

根據 CAB Forum 的規定,憑證期限縮短規劃如下:

  • 至 2026 年 3 月 15 日止,TLS 憑證的最長有效期為 398 天。
  • 自 2026 年 3 月 15 日起,TLS 憑證的最長有效期為 200 天。
  • 自 2027 年 3 月 15 日起,TLS 憑證的最長有效期為 100 天。
  • 自 2029 年 3 月 15 日起,TLS 憑證的最長有效期為 47 天。

這也就意味著傳統的憑證申請流成將被淘汰,全面走向自動化申請。

有了憑證之後,客戶端和伺服器是怎麼建立安全連線的呢?這就是 TLS 握手的工作。

目前主流的 TLS 1.3 握手只需要 1-RTT(一次來回),比舊版 TLS 1.2 的 2-RTT 更快:

sequenceDiagram
    participant C as 客戶端
    participant S as 伺服器

    C->>S: ① Client Hello
    Note right of C: 支援的加密套件清單、亂數、DH 公鑰

    S->>C: ② Server Hello
    Note left of S: 選定的加密套件、亂數、DH 公鑰

    Note over C,S: 雙方各持對方的 DH 公鑰,計算出相同的 Session Key<br/>(金鑰不透過網路傳遞!)

    S->>C: ③ Certificate(伺服器憑證)
    S->>C: ④ CertificateVerify(用伺服器私鑰簽名)
    S->>C: ⑤ Finished

    Note over C: 用 CA 信任鏈驗證憑證合法性<br/>用憑證中的公鑰驗證伺服器簽名

    C->>S: ⑥ Finished

    Note over C,S: 握手完成,使用 Session Key 加密後續所有資料

整個握手流程有三個核心目標:

  • 身份驗證:透過憑證和 CA 信任鏈確認伺服器身份,防止中間人攻擊
  • 金鑰交換:使用 Diffie-Hellman(DH) 演算法,讓雙方在不直接傳送密鑰的情況下算出相同的 Session Key
  • 資料加密:握手完成後,所有資料都以 Session Key 做對稱加密傳輸,效率遠高於非對稱加密

不過還是有個缺陷,就是 Client Hello 是用明文傳送的,且其中包含 SNI(Server Name Indication),可以讓中間人看出客戶端在連接的網站。

那我們到底要怎麼把 SSL 憑證安裝到我們的網站上呢?

好消息是現在有很多免費的 SSL 憑證提供者,例如 Let’s Encrypt、Google Trust Services 等等,這些提供者都提供了自動化的工具,可以輕鬆地申請和安裝 SSL 憑證。 不像以前憑證都需要付費,還要手動申請和安裝,現在只需要幾行指令就可以完成整個流程。

那他們要怎麼驗證我們是網站的擁有者呢?

Section titled “那他們要怎麼驗證我們是網站的擁有者呢?”

我們需要從下面幾種挑戰中選擇一種來完成驗證:

  • HTTP-01:在網站上放置一個特定的檔案,讓 CA 來訪問這個檔案來驗證對該域名的控制權。
  • DNS-01:在 DNS 記錄中添加一個特定的 TXT 記錄,讓 CA 來查詢這個記錄來驗證對該域名的控制權。
  • TLS-ALPN-01:在伺服器上配置一個特定的 TLS 服務,讓 CA 來連接這個服務來驗證對該域名的控制權。

以下以 HTTP-01 為例:

  1. 我們需要先有一個網域,只有 ip 是無法申請 SSL 憑證的,因為憑證是綁定在網域上的。

  2. 接著我們需要選擇一個 SSL 憑證提供者,我推薦 Let’s Encrypt,因為它是免費的,而且有很多自動化的工具可以使用。

  3. 接著我們需要安裝自動化工具,例如 Certbot,來幫助我們自動申請和安裝 SSL 憑證。

    Terminal window
    sudo snap install --classic certbot
    sudo ln -s /snap/bin/certbot /usr/local/bin/certbot
  4. 申請憑證!

    Terminal window
    sudo certbot certonly --standalone -d yourdomain.com
  5. 測試自動續約(可選)

    Terminal window
    sudo certbot renew --dry-run

或者是我也很推薦直接使用 Caddy 作為反向代理伺服器,因為他內建了自動申請和安裝 SSL 憑證的功能,完全不需要額外的工具和設定,且設定檔也比較簡單。

如果想要快速體驗的話可以把任何網域的 caddydemo 子網域指向 Caddy demo.caddyserver.com,接著直接在瀏覽器打開它,就能親身體驗 Caddy 自動申請 SSL 憑證的高效及便利。
例如:https://caddydemo.doong.me/
夯爆了,用過一次就回不去了。

雖然我們有了憑證,但要將他套用到我們的網站上是很麻煩的,我們很難直接依靠一個 fastapiflask 或其他框架的應用程式來處理 SSL/TLS。

因此,我們通常會使用一個反向代理伺服器(例如 Caddy、Nginx、Apache 等等)來處理 SSL/TLS,然後將請求轉發到我們的應用程式上。

反向代理是將本地的 http 服務轉發成外部可連線的 https 服務。此外,反向代理還可以提供負載平衡、緩存、壓縮等功能,提升網站的效能和安全性。

我絕對推薦 Caddy,他的設定檔非常簡單,且自動配置 SSL 憑證的功能真的太方便了,自從我用了 Caddy 後,我再也不用擔心 SSL 憑證的問題了。 Caddy 真的用過就回不去了。

雖然他在極端的高流量情況下可能會有一些性能可能略低於 Nginx,但明顯我不會遇到這麼大的流量。

Caddy

如果沒有自己的伺服器,或者伺服器沒有公開的 IP 地址,那麼就無法直接申請 SSL 憑證了。

不過可以使用一些隧道服務(例如 ngrok、Cloudflare Tunnel 等等)來將本地服務暴露到外部,這些服務也會自動搞定 SSL 憑證。 且這些隧道服務通常也會提供一些額外的功能,例如流量分析、DDoS 防護、訪問控制等等,非常方便。

最推 Cloudflare Tunnel,蠻好用的。 但如果沒有自己的網域,可能只能選擇 ngrok 的免費子網域了,雖然不太好記,但也是一個不錯的選擇。