Cloudflare Tunnel 與應用層端對端加密(E2EE)技術白皮書

本站使用 Cloudflare Tunnel 遠端管理伺服器的動態 IP 白名單工具。這時,控制平面的認證安全性非常重要。本文說明逆向代理環境下的信任邊界。本文也說明如何用 ECDH 和 AES-256-GCM 建立零信任的應用層加密通道。

1. 為什麼需要 Payload 加密

在標準的 Cloudflare Tunnel 部署中,邊緣節點必須認證、路由並清洗客戶端的 HTTPS 流量。

信任邊界:因此,Cloudflare 可以存取 HTTP 的明文內容。如果管理端 API 直接傳輸明文的帳號、密碼或敏感的 Ingest Token,這些資料會以明文出現在邊緣節點的記憶體中。嚴格的零信任架構不可以有這種情況。

2. 威脅模型(Threat Model)

本系統的密碼學架構有明確的防禦範圍:

本系統防範(In-Scope) 本系統不防範(Out-of-Scope)
全球背景的惡意掃描(Internet Background Scan) 伺服器作業系統已被攻陷(Host Compromised)
針對未開放 port 的精準掃描(Port Scan) 核心態惡意程式碼植入(Kernel Rootkit)
中間人攔截,以及未授權的 Publish 嘗試 上游軟體供應鏈污染(Supply Chain Attack)
針對管理端入口的字典爆破和密碼撞庫 密碼學基礎演算法的 0-Day 漏洞
邊緣逆向代理節點直接讀取敏感資料 惡意擴充套件在瀏覽器端攔截資料

3. Cloudflare Tunnel 傳輸拓撲

下圖說明資料在網路傳輸過程中,加密狀態如何改變:

flowchart LR
A[已授權使用者的瀏覽器] -->|1. 外部 HTTPS 加密| B[Cloudflare Edge]
B -->|2. 內部 Tunnel 加密| C[cloudflared 常駐程式]
C -->|3. 127.0.0.1 本機明文| D[Node.js 控管服務]
          
本機通訊安全:第 3 段的明文傳輸只發生在主機內部的 Loopback 介面。外部線路無法嗅探這段傳輸。

延伸閱讀:AWS、GCP 和 Azure 幾乎沒有和 Cloudflare Tunnel 直接對應的服務。差異整理在雲端服務對照表。

4. 為什麼傳統 TLS 還不夠

傳統 TLS(HTTPS)只保證「瀏覽器到 Cloudflare Edge」這段線路的機密性。Edge 執行 TLS Termination(TLS 解密截斷) 之後,應用層封包以明文暴露在 Edge。逆向代理服務商持有 TLS 的金鑰,所以可以讀取這些封包。

5. ECDH 金鑰交換原理

要實現真正的端對端安全,敏感 Payload 必須在瀏覽器端加密。Payload 必須進入本機 Node.js 之後才可以解密。

系統使用 ECDH(橢圓曲線迪菲-赫爾曼金鑰交換) 的 P-256 曲線。伺服器持有固定的金鑰對。客戶端每次發出請求時,臨時產生一對短暫金鑰(Ephemeral Key)。短暫金鑰確保前向保密。

密碼學隔離結論:雙方用數學運算得到相同的共享秘密(Shared Secret),再從共享秘密衍生出對稱加密金鑰。Cloudflare 無法取得應用層 Payload 的明文。

6. AES-256-GCM 應用層對稱加密

取得共享秘密並衍生對稱金鑰後,系統不使用安全性較低的傳統 CBC 模式,全面使用 AES-256-GCM(伽羅瓦/反饋計數器模式)。

  • 認證加密(AEAD):GCM 模式加密時,同時產生 128-bit 的認證標籤(Authentication Tag)。認證標籤可以有效防範密文被惡意竄改和重放攻擊。
  • 唯一隨機向量:每次加密都必須使用唯一的 96-bit 初始化向量(IV/Nonce)。這樣可以消除密文的特徵規律。

7. Web Crypto API 原生實作

本系統只使用執行環境原生的密碼學核心。本系統不使用任何未經審查的第三方自製加密庫:

// 在瀏覽器端用標準 Web Crypto API 加密
const sharedSecret = await window.crypto.subtle.deriveBits(
  { name: "ECDH", public: serverPublicKey },
  clientPrivateKey,
  256
);

const aesKey = await window.crypto.subtle.importKey(
  "raw", sharedSecret, { name: "AES-GCM" }, false, ["encrypt"]
);

const ciphertext = await window.crypto.subtle.encrypt(
  { name: "AES-GCM", iv: nonce },
  aesKey,
  new TextEncoder().encode(JSON.stringify(apiBody))
);

8. 完整資料時序流圖

sequenceDiagram
  autonumber
  participant B as 瀏覽器 (Client)
  participant CF as Cloudflare Edge
  participant S as Node.js 伺服器 (Origin)

  B->>S: 取得伺服器的 ECDH 公鑰
  S-->>B: 傳回公鑰 (固定)
  Note over B: 1. 本地生成臨時金鑰對
2. 計算出 Shared Secret
3. AES-GCM 加密明文 Payload B->>CF: 傳送 HTTP POST (Body 已是密文) Note over CF: TLS Termination 解密
僅能看見密文外殼與隨機公鑰 CF->>S: 經由 Tunnel 轉發已加密的 Body Note over S: 1. 提取客戶端臨時公鑰
2. 計算出 Shared Secret
3. AES-GCM 解密並驗證 Tag
4. Hot Reload 寫入設定檔

9. 密碼學安全性分析

逆向代理節點(Cloudflare)可以得知的資產 逆向代理節點(Cloudflare)無法得知的資產
請求 URL 路徑、HTTP Method、HTTP Headers 使用者輸入的明文管理員密碼
現場端瀏覽器的真實來源公網 IP 由金鑰衍生出的 Session Token
通訊發生的時間點,以及精確的封包大小 即將寫入 MediaMTX 設定的真實 IP 清單
TLS SNI 特徵標記 任何結構化的內部 JSON 設定物件

10. 系統限制與潛在威脅

  • 初次信任風險(TOFU):客戶端第一次取得伺服器的靜態公鑰時,可能遭遇高度精準的中間人攻擊(MITM)。如果發生,端對端加密連線在理論上可能被破解。
  • 元資料洩漏(Metadata Leakage):Payload 的內容受到保護。但是,攻擊者仍然可以做流量分析(Traffic Analysis)。攻擊者依據封包大小和時序特徵,可以推斷控制行為何時發生。

11. 金鑰管理維護檢查清單

  • 伺服器私鑰檔案 server-ecdh-keys.json 的權限必須設為 chmod 600。
  • 不可以把包含伺服器真實私鑰的設定上傳到任何公開的 GitHub 儲存庫,也不可以上傳到沒有加密的雲端備份碟。
  • 建議每 180 天主動輪換一次伺服器端的 ECDH 金鑰對(Key Rotation)。
  • 控制端 API 入口必須設定速率限制(Rate Limit)。速率限制可以防止針對密文標籤的主動重放探測。