mTLS 這篇要解決什麼

mTLS 的核心是把系統身分綁到 X.509 憑證與私鑰,而不是可重用的 shared secret。介紹文章常把它簡化成「雙向 TLS 憑證、適合金融醫療」,但實際落地時,設計責任會立刻延伸到 CA 階層、憑證生命週期、撤銷與基礎設施整合:

  • 自簽 CA 還是商業 CA?
  • 憑證放哪、怎麼 rotate?
  • 怎麼撤銷?CRL 還是 OCSP 還是 short-lived cert?
  • nginx 設定怎麼寫、service mesh 怎麼整合?
  • 跟 API Key、OAuth 比,什麼情境適合承擔 mTLS 的運維成本?

這些是 mTLS 第一次部署就要處理的基本問題。若只知道「雙向憑證」而沒有 lifecycle 設計,系統會在過期、撤銷或 mesh 升級時失去可預測性。

本文拆解 mTLS 的工程實務:

  1. CA 階層:為什麼要分層、Root CA / Intermediate CA / Leaf cert
  2. 憑證生命週期:簽發、儲存、rotation、撤銷
  3. 基礎設施整合:nginx / envoy / service mesh 設定模式
  4. 跟其他 Layer 2 方案的取捨:何時 mTLS 才是對的選擇

本文位置:本文是 API 認證的三層信任邊界 Layer 2 的深入篇之一。主文聚焦「為什麼系統間要獨立 credential」、本文聚焦「用 mTLS 實作這層的具體工程細節」。


mTLS 解什麼問題

跟一般 TLS 的差異

一般 TLS(HTTPS)是單向認證:client 驗證 server 身分,server 再透過 API Key、token 或 session 辨識 client。

1client ────"我要連 example.com"────▶ server
2       ◀───server 出示憑證───────── server
3       驗證:"這是真的 example.com 嗎"
45       建立加密通道

client 驗證 server、但 server 不驗證 client。Client 是匿名的、靠後續 API Key / token 認證。

mTLS 加上反向驗證:server 也在 TLS handshake 階段驗證 client 憑證,把系統身分提前到連線層建立。

1client ──"我要連 example.com、這是我的憑證"──▶ server
2       ◀──server 出示憑證───────────────────── server
3       
4       雙方驗證對方憑證:
5       client: "這是真的 example.com 嗎"
6       server: "這個 client 是被授權的嗎"
78       建立加密通道、且雙方都已認證

每個 client 有自己的憑證、server 用 CA 信任鏈驗證 client 憑證是否合法。Client 的身分綁定在 X.509 憑證上、不需要額外的 API Key

mTLS 解的具體威脅

威脅一般 TLS + API KeymTLS
中間人攔截TLS 已解TLS 已解
攻擊者用洩漏的 API Key 假冒 client需 client 私鑰、無法只憑網路觀察取得
API Key 寫在 client code、被反編譯私鑰可放硬體(HSM / TPM / Secure Enclave)
Server 端 per-client credential 被攻陷漏(API Key DB 外流)server 無 per-client secret、僅 CA trust chain 暴露
Client 端被植入、用合法身分滲透部分(rate limit)同樣(需依靠撤銷機制)

mTLS 的核心優勢是:client 端的 private key 是 scope-bound、不跨系統共用。私鑰理論上不離開 client,且驗證憑藉的是 CA 簽章而非可重用字串;相較 shared API Key,一個 client 的私鑰外流通常可被限制在該 client 的憑證與授權範圍內。

代價是:PKI 基礎建設複雜、憑證生命週期管理重、運維成本高。


CA 階層設計

為什麼要分層

CA 分層的核心責任是降低最高信任根的暴露頻率。直覺做法是「用一張 Root CA 直接簽 client 憑證」:

1Root CA ──signs──▶ client-A.crt
2        ──signs──▶ client-B.crt
3        ──signs──▶ client-C.crt
4        ...

Root CA 私鑰是整個 PKI 的最高信任根,通常需要離線、HSM 與多人簽核。它一旦洩漏,所有信任這個 Root 的系統都要重新建立信任;Root CA 又通常活 10-20 年,撤換成本極高。

如果 Root CA 私鑰要常常拿出來簽 client cert、暴露風險就大幅提高。

解法:分層。Root CA 只簽 Intermediate CA、Intermediate CA 負責日常簽發 client cert:

1Root CA (offline, 20 年)
2    ↓ signs (一次性 / 5-10 年)
3Intermediate CA (online, 1-5 年)
4    ↓ signs (日常、每張 90 天-1 年)
5Leaf certificates (client / server)

Root CA 通常完全離線(air-gapped 機器、硬體 HSM)、私鑰一年只拿出來簽幾次(簽 Intermediate)。Intermediate CA 才是 online、處理日常簽發。

階層帶來的好處

好處機制
Root CA 私鑰暴露次數降到最低只在簽 Intermediate 時用、其他時間離線
Intermediate 被攻陷可撤換Root CA 撤掉該 Intermediate、用新 Intermediate 簽
可按用途分 Intermediate一個給 server cert、一個給 client cert、一個給 internal services
短 chain 仍可驗證client 只信任 Root CA、Intermediate 在 chain 中傳遞

三種典型部署模式

模式 A:自管 CA

完全自己跑 CA infra:

  • Root CA:離線 HSM、年度作業簽 Intermediate
  • Intermediate CA:online、用工具如 step-cacfsslVault PKISmallstep
  • Leaf cert:自動化簽發、短 TTL

適合:純內部系統、不需 public trust、要完全控制 CA infrastructure。

模式 B:商業 CA(DigiCert / Sectigo / Entrust)

買商業 CA 服務、商業 CA 已預埋進所有 OS / browser trust store:

  • 適合:需要 public trust(HTTPS server cert、SSL/TLS for end users)
  • mTLS client cert 通常在自己的封閉系統內驗證,public trust 的價值較低,因此較少使用商業 CA

模式 C:Cloud-managed PKI

雲廠商提供 managed PKI:

  • AWS Private CA(ACM PCA)— managed Root + Intermediate
  • GCP Certificate Authority Service
  • Azure Key Vault Certificates

適合:已在某朵雲、不想自管 CA infra、可接受 vendor lock。

自管 CA 的最小工具鏈

如果走模式 A、推薦工具:

工具用途特性
step-caLightweight CA server、支援 ACMESmallstep 開源、設定簡單
HashiCorp Vault PKIVault 內建 PKI engine整合 Vault 既有 secret 管理
cfsslCloudflare 的 CA toolkitCLI-based、適合 build pipeline
OpenSSL純手工建 CA維運成本高、適合學習與小規模

step-ca 是最低門檻的起手選擇 — 一行 step ca init 建好整套 CA、自動發 ACME 給 client。


憑證生命週期

簽發

Server cert 簽發流程

11. Server 產生 private key (RSA 2048+ 或 ECDSA P-256)
22. Server 用 private key 產生 CSR (Certificate Signing Request)
33. CSR 送給 CA
44. CA 驗證 CSR 內容(DN、SAN、用途)
55. CA 用 Intermediate CA 私鑰簽 cert
66. 把簽好的 cert 回給 server
77. Server 部署 cert + 自己的 private key

Client cert 簽發流程:跟 server 一樣,但 SAN 通常是 client identifier(service name、device ID),而非 hostname。

私鑰留在產生端

關鍵安全原則是:private key 在哪產生、就只在那裡存活。CA 只收 CSR(裡面只有 public key),簽完 cert 回去;client private key 全程留在 client 的受控環境。

失效模式

  • CA 幫 client 產生 keypair、把 private key 跟 cert 一起寄給 client(密鑰在 CA 經手了)
  • 把 private key 跟 cert 打包成 PKCS12 用 email 寄
  • 把 keypair 放進公共 git repo

操作路由

  • Client 端產生 keypair、只送 CSR 給 CA(CSR 只含 public key)、簽完 cert 回來、private key 全程不離開 client

儲存

Private key 的儲存等級:

方式安全等級適合
Plain file(chmod 600)dev / staging、無 HSM 的低風險環境
OS keystore(Keychain / Windows Cert Store)desktop client、laptop
HSM(hardware security module)金融、政府、私鑰永不離開硬體
Cloud KMS(AWS KMS / GCP KMS)中-高cloud-native、private key 進 KMS、簽章用 API
TPM / Secure Enclavemobile / IoT、跟硬體綁定

Production server cert 私鑰至少應該 OS 層保護(檔案權限 + 加密磁碟)、高敏感場景上 HSM。

Rotation

mTLS 憑證的 rotation 跟 shared secret rotation 概念類似、但有具體差異:

維度Shared SecretmTLS Cert
過期機制沒有、要手動 rotate內建 notBefore / notAfter、自動過期
雙密期兩把同時 valid過渡期 server 同時持有舊 cert(未過期)+ 新 cert(已簽發)、自動有效
Rotation 觸發排程排程 + 過期前自動

實務上的 rotation 模式:

短 TTL + 自動續發(推薦)

  • Leaf cert TTL 設短(24 小時 ~ 7 天)
  • 用 ACME protocol(如 Let’s Encrypt 的協定)讓 client 自動續發
  • rotation 由續發流程承擔,過期前自動換新

工具:cert-manager(K8s)、step-ca + stepcertbot

中 TTL + 半自動(傳統)

  • TTL 1 年、年度手動 rotation
  • 用工具列管所有 cert 的 notAfter、過期前 30 天自動告警
  • 適合舊架構、無法跑短 TTL 的場景

長 TTL(不建議)

  • TTL 多年、近乎不 rotate
  • 私鑰暴露窗極長、被洩漏到察覺的時間差大
  • 唯一情境:IoT 設備、無法 OTA 更新

撤銷

當 cert 在 notAfter 前需要失效(私鑰洩漏、員工離職、合約終止)、需要撤銷機制。有三種主流方案:

CRL(Certificate Revocation List)

CA 維護一份「已撤銷憑證 list」、定期發佈(小時級到天級)。Client 端要:

  1. 下載最新 CRL
  2. 連線時檢查對方 cert 是否在 CRL 內

優點:簡單、infrastructure 輕。

缺點

  • CRL 大、下載成本高
  • Cache 期內撤銷不生效(最差幾小時)
  • Client 沒下載 CRL、撤銷完全沒效

OCSP(Online Certificate Status Protocol)

Real-time 查詢、client 每次連線時即時 query OCSP responder:「這張 cert 還有效嗎?

優點:Real-time、撤銷即時生效。

缺點

  • 每次連線增加一次 OCSP query、延遲
  • OCSP responder 是 single point of failure
  • Privacy 顧慮(每次連線都告訴 CA 你在連誰)

進階:OCSP Stapling — server 預先 query OCSP、把結果 staple 在自己的 cert chain 裡、client 不需自己 query。解決延遲跟 privacy、但 server 端要實作。

Short-lived cert(不撤銷、讓它過期)

最現代的做法:cert TTL 極短(小時、甚至分鐘)、不實作撤銷機制、靠過期自然失效

優點

  • 可省略 CRL / OCSP infrastructure
  • 撤銷窗 = TTL(小時級)、可預期
  • Privacy 友善

缺點

  • 需要可靠的自動續發機制
  • Client 無法續發時直接斷線

工具:SPIFFE/SPIRE 主推這個模式、cert TTL 設小時級。

三種撤銷方案的選擇

場景推薦撤銷方案
傳統 enterprise、架構變動成本高CRL(最低門檻)
公開 HTTPS、需要 real-time 撤銷OCSP Stapling
Cloud-native、有自動續發 infraShort-lived cert
內部 service meshShort-lived cert(mesh 自動)

基礎設施整合

nginx 設定 mTLS server

最常見的場景:nginx 當 reverse proxy、要求 client 出示憑證。

 1server {
 2    listen 443 ssl;
 3    server_name api.example.com;
 4
 5    # Server cert (出示給 client)
 6    ssl_certificate     /etc/ssl/certs/api.crt;
 7    ssl_certificate_key /etc/ssl/private/api.key;
 8
 9    # 要求 client 出示憑證、用這個 CA 驗證
10    ssl_client_certificate /etc/ssl/ca/client-ca-chain.pem;
11    ssl_verify_client on;            # 強制 client 出示憑證、否則拒絕
12    ssl_verify_depth 2;              # 驗證 chain 深度、視 PKI 階層調 (Root → Intermediate → Leaf)
13
14    location / {
15        # 把 client cert 資訊傳給後端 application
16        proxy_set_header X-Client-DN  $ssl_client_s_dn;
17        proxy_set_header X-Client-Verify $ssl_client_verify;
18        proxy_pass http://backend;
19    }
20}

關鍵 directive:

Directive作用
ssl_client_certificate信任的 CA chain
ssl_verify_client on強制 client 出示憑證、optional 則彈性接受
ssl_verify_depthchain 驗證深度、根據 PKI 階層調
$ssl_client_s_dn傳 client cert 的 subject DN 給 backend

nginx 設定 mTLS client(呼叫上游)

當 nginx 是 client、要呼叫上游 mTLS server:

1location /upstream {
2    proxy_pass https://upstream.example.com;
3    proxy_ssl_certificate     /etc/ssl/certs/client.crt;
4    proxy_ssl_certificate_key /etc/ssl/private/client.key;
5    proxy_ssl_trusted_certificate /etc/ssl/ca/upstream-ca.pem;
6    proxy_ssl_verify on;
7}

Envoy / API Gateway 整合

Envoy 是 service mesh 的常見 data plane、mTLS 設定模式:

 1listeners:
 2- name: api_listener
 3  address: { socket_address: { port_value: 443 } }
 4  filter_chains:
 5  - transport_socket:
 6      name: envoy.transport_sockets.tls
 7      typed_config:
 8        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
 9        common_tls_context:
10          tls_certificates:
11          - certificate_chain: { filename: /etc/ssl/api.crt }
12            private_key:      { filename: /etc/ssl/api.key }
13          validation_context:
14            trusted_ca: { filename: /etc/ssl/client-ca.pem }
15        require_client_certificate: true

上方只展 inbound listener 的 DownstreamTlsContext。Envoy 作為 client 呼叫上游 mTLS server 時、要在對應的 cluster 配 transport_socket + UpstreamTlsContext(含 client cert + private key + trusted CA)、不在這份 listener 設定裡。

跟 nginx 比、Envoy 的優勢:

  • 動態設定(xDS API、不需 reload)
  • 支援 SDS(Secret Discovery Service)動態取憑證
  • 跟 Istio / Linkerd 等 mesh 整合

Service Mesh(Istio / Linkerd)

Service mesh 內建 mTLS:

1# Istio: 強制 mesh 內所有 service 走 mTLS
2apiVersion: security.istio.io/v1beta1
3kind: PeerAuthentication
4metadata:
5  name: default
6  namespace: production
7spec:
8  mtls:
9    mode: STRICT

機制:

  • Mesh control plane(Istio: Istiod / Linkerd: identity)內建 CA、自動發每個 pod 一張 cert
  • Sidecar proxy(Envoy / Linkerd proxy)handle TLS termination、application code 完全不感
  • Cert TTL 短(Istio 預設 24 小時、視版本而定)、自動續發
  • mTLS identity 綁定 K8s ServiceAccount

優點:application 完全不用改 code、不用管 cert、不用管 rotation — mesh 全包。

缺點:綁定整套 mesh 架構、運維 mesh 本身是大事、學習曲線陡。

為 application 直接做 mTLS

某些場景(沒 mesh、需要 application 級控制)需要 application 直接做 mTLS:

1# Python requests 範例 - mTLS client
2import requests
3
4response = requests.get(
5    'https://api.example.com/data',
6    cert=('/path/to/client.crt', '/path/to/client.key'),
7    verify='/path/to/server-ca.pem',
8)
 1// Go net/http 範例 - mTLS client
 2cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
 3if err != nil { return err }
 4
 5caCert, err := os.ReadFile("server-ca.pem")
 6if err != nil { return err }
 7caCertPool := x509.NewCertPool()
 8caCertPool.AppendCertsFromPEM(caCert)
 9
10client := &http.Client{
11    Transport: &http.Transport{
12        TLSClientConfig: &tls.Config{
13            Certificates: []tls.Certificate{cert},
14            RootCAs:      caCertPool,
15        },
16    },
17}
18resp, err := client.Get("https://api.example.com/data")

每個語言的 stdlib 都有對應 API、寫法大同小異。但 application 要自己處理 cert reload、過期、rotation — 比 service mesh 麻煩很多。


跟其他 Layer 2 方案的成本比較

mTLS 在三層信任邊界的 Layer 2 是安全強度高、運維責任也重的選項。是否採用,要看威脅模型、合規要求、私鑰保護能力與自動化成熟度。

方案安全等級運維成本適合
Shared Secret低-中純內部、低風險
API Key + HTTPS一般 SaaS、對外 API
HMAC 簽章中-高需防 replay / tampering
OAuth Client Credentials中-高跨組織、需 short-lived token
mTLS合規、零信任、私鑰可硬體保護

mTLS 適合的場景

場景為什麼 mTLS 適合
金融、醫療、政府合規要求合規條款直接要求 mTLS
零信任網路(zero-trust)網路不可信、每個 hop 都要驗身分
內部 service mesh(K8s + Istio)Mesh 自動處理、邊際成本低
私鑰能放硬體(HSM / TPM / Secure Enclave)比 API Key 強得多
高頻 service-to-service、API Key rotation 痛苦短 TTL cert 自動續發、不用人介入

mTLS 成本偏高的場景

場景成本偏高的原因
對外開放給第三方 SDK第三方管理 cert 的門檻高、API Key + HTTPS 較易落地
小規模、運維資源少PKI infra 維護成本超過安全增益
純內部、不需強身分隔離Shared secret 已經夠用
大量短連線 client(mobile app)Cert 散佈跟 rotation 複雜度高

常見失敗模式

失敗 1:忘記 Intermediate CA、chain 不完整

症狀:server 設定看似正確、但 client 連線時報 certificate verify failed

根因:server 端只放了 leaf cert、沒附 Intermediate CA。Client 端只信任 Root、無法 chain 到 Root。

緩解:server 端 ssl_certificate 要放完整 chain(leaf + intermediate、不含 root):

1cat leaf.crt intermediate.crt > chain.crt
2# nginx 用 chain.crt 而非單獨 leaf.crt

失敗 2:Cert 過期造成連線中斷

症狀:cert notAfter 過了、所有 client 突然連不上。

緩解

  • 監控 cert 過期時間、提前 30 天告警、提前 7 天緊急告警
  • 用自動續發機制(cert-manager / step-ca / ACME)
  • 過期防護應由系統監控與自動續發承擔,而不是依賴人工記憶

失敗 3:私鑰權限過寬、被同機其他 user 讀走

症狀:security audit 發現 /etc/ssl/private/server.key 是 644、所有 user 可讀。

緩解

  • Private key 一律 chmod 600、owner root 或 application user
  • 用 systemd 跑的 service、private key 放 LoadCredential= 而非 file path
  • 定期 audit /etc/ssl/ 權限

失敗 4:撤銷後 cert 仍能用

症狀:cert 撤銷了、但 client 還能連上。

根因

  • CRL 設定但 server 沒 enable CRL check
  • OCSP 設定但 client 沒 query
  • 用 short-lived cert 但 TTL 太長、撤銷窗不可接受

緩解:撤銷機制要端到端測試、不只「設定上有」、要驗證「實際生效」。

失敗 5:Service mesh upgrade 後 mTLS 中斷

症狀:Istio 升級後、cluster 內部分 service 互相連不上。

根因:mesh control plane 的 CA 換了、舊 cert chain 不通。

緩解

  • Mesh upgrade 走 staged rollout,分批驗證 cert chain
  • Mesh 提供的 CA migration 流程要完整執行
  • Staging 環境先跑升級流程

收尾

mTLS 是「用 PKI 換掉 secret 管理」的設計 — 私鑰不離 client、身分綁在 X.509 cert 上、不依賴可重用的字串。安全等級高、但代價是要建立 CA infrastructure、處理 cert 生命週期、整合到各種基礎設施。

幾個核心判斷:

  1. CA 分層是基本盤 — Root + Intermediate + Leaf,讓最高信任根維持低暴露
  2. 私鑰留在產生端 — CA 只簽 CSR、不碰 private key
  3. 撤銷方案要實證可用 — CRL / OCSP / Short-lived 三選一,並驗證實際生效
  4. Service mesh 是 cloud-native 的低成本入口 — Istio / Linkerd 把 mTLS 變成基礎設施,application 改動較小
  5. mTLS 是高責任方案 — 對外開放 API、小規模、無 mesh 場景,OAuth / API Key 往往更容易維運

延伸閱讀: