Contents

SSL Pinning 和 HSTS 小記

HTTPS 加密保護了傳輸中的資料,但攻擊者仍可能以偽造憑證發動中間人攻擊(Man-in-the-Middle Attack)。SSL Pinning 和 HSTS 是兩種進一步強化 HTTPS 安全性的技術,分別從客戶端和伺服器端著手防禦。

SSL Pinning(憑證綁定)

什麼是 SSL Pinning?

標準的 HTTPS 連線信任作業系統的「根憑證授權機構(Root CA)」清單。只要憑證是由受信任的 CA 簽發,連線就會被接受。攻擊者如果能在設備上安裝偽造的 CA 憑證(如某些企業 Proxy 或惡意工具),就能解密並竄改 HTTPS 流量。

SSL Pinning 的做法是讓應用程式(通常是 App)在連線時額外驗證伺服器憑證的特定資訊(「釘選」特定憑證),而不只依賴系統的 CA 清單。即使攻擊者的 CA 憑證已安裝在設備上,只要憑證指紋不符,連線就會被拒絕。

Pinning 的兩種方式

Certificate Pinning(憑證 Pinning)

直接比對整張憑證的內容或 SHA256 指紋(fingerprint):

1
2
# 計算憑證指紋
openssl x509 -in certificate.pem -pubkey -noout | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | openssl enc -base64

缺點:憑證每次更換(通常 1-2 年)時,App 必須同步更新,否則所有舊版 App 連線都會失敗。

Public Key Pinning(公鑰 Pinning,推薦)

只比對憑證中的公鑰(Public Key),不比對整張憑證:

1
2
# 從憑證提取公鑰指紋
openssl x509 -in certificate.pem -pubkey -noout | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | base64

優點:更換憑證時,只要保持相同的金鑰對(CSR),公鑰就不會變,App 不需要更新。

Android 實作(Network Security Config)

Android 7.0+ 支援在 XML 中設定 Pinning:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">api.example.com</domain>
        <pin-set expiration="2025-01-01">
            <!-- sha256/ 後接 base64 編碼的公鑰指紋 -->
            <pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
            <!-- 備用 pin,防止主憑證失效 -->
            <pin digest="SHA-256">BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

AndroidManifest.xml 中引用:

1
<application android:networkSecurityConfig="@xml/network_security_config">

iOS 實作(URLSession)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 實作 URLSessionDelegate
extension APIClient: URLSessionDelegate {
    func urlSession(_ session: URLSession,
                    didReceive challenge: URLAuthenticationChallenge,
                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
              let serverTrust = challenge.protectionSpace.serverTrust else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }

        // 取出伺服器憑證的公鑰並比對
        // ...(完整實作需提取公鑰並比對預先儲存的指紋)
        completionHandler(.useCredential, URLCredential(trust: serverTrust))
    }
}

SSL Pinning 的注意事項

  • 備用 Pin:至少設定 2 個 Pin(主要 + 備用),防止更換憑證時所有使用者連線失敗
  • 設定到期日:Android 的 expiration 可設定 Pin 的有效期,到期後退回正常驗證
  • 開發 / 測試環境:可根據 BuildType 決定是否啟用 Pinning,避免測試環境被 Pinning 阻擋
  • 繞過工具:Frida、Objection 等工具可在已越獄設備上繞過 SSL Pinning,Pinning 不是無敵的

HSTS(HTTP Strict Transport Security)

什麼是 HSTS?

HSTS 是一個 HTTP Response Header,告訴瀏覽器:「之後一段時間內,不管使用者輸入什麼,都必須用 HTTPS 連線到本網站」。

這解決了 SSL Stripping 攻擊:攻擊者在用戶首次連線時(使用 HTTP)攔截流量,降級為 HTTP,讓 HTTPS 保護形同虛設。

HSTS Header 設定

在 Web 伺服器的 HTTPS 回應加入:

1
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

參數說明:

參數 說明
max-age=31536000 瀏覽器記住此規則 1 年(31536000 秒)
includeSubDomains 同樣適用於所有子網域
preload 申請加入瀏覽器內建的 HSTS 預載清單

Nginx 設定範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
server {
    listen 443 ssl;
    server_name example.com;

    # SSL 設定...

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

# 將 HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Apache 設定範例

1
2
3
<VirtualHost *:443>
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</VirtualHost>

HSTS Preload

HSTS Preload 是一個由瀏覽器廠商維護的清單,主流瀏覽器(Chrome、Firefox、Safari)內建這份清單。被加入清單的網域,即使是第一次連線也強制使用 HTTPS,完全消除首次連線的風險。

申請網址:hstspreload.org

申請條件:

  • 有效的 SSL 憑證
  • 所有 HTTP 流量重定向到 HTTPS
  • 所有子網域也必須支援 HTTPS
  • HSTS Header 的 max-age 至少 1 年,並包含 includeSubDomains; preload

⚠️ 警告:加入 Preload 後很難撤銷,確保所有子網域都能穩定支援 HTTPS 再申請。

兩者的應用場景

技術 防禦對象 適用場景
SSL Pinning 偽造 CA 憑證的中間人攻擊 行動 App、金融/醫療等高安全需求
HSTS SSL Stripping、HTTP 降級攻擊 所有需要強制 HTTPS 的網站

參考資料