加密架構¶
券商憑證是平台中最敏感的資料。它們直接授予對持有真實資金的交易帳戶的存取權限。這些憑證必須在靜態儲存、傳輸過程和處理期間全程加密。私鑰永遠不離開硬體。
4pass 實作了三層加密金鑰層級,並具備每位使用者隔離機制,確保一位使用者資料的外洩不會擴散到其他使用者。
加密金鑰層級¶
flowchart TB
subgraph HSM["AWS KMS HSM (FIPS 140-2 Level 3)"]
MASTER["主金鑰<br/>(RSA-4096 或 AES-256)<br/>不可匯出"]
end
subgraph Server["應用程式伺服器"]
ACCOUNT_KEY["每帳戶加密金鑰<br/>32 位元組 AES-256 金鑰<br/>(每個交易帳戶獨立)"]
DECRYPT["已解密憑證<br/>(僅存於記憶體,不持久化)"]
end
subgraph DB["PostgreSQL"]
ENC_ACCOUNT_KEY["加密的帳戶金鑰<br/>(由主金鑰封裝)"]
ENC_CREDS["加密的憑證<br/>AES-256-GCM 密文<br/>+ 12 位元組 Nonce + GCM 標籤"]
end
MASTER -->|"信封解密<br/>(KMS API 呼叫)"| ACCOUNT_KEY
ENC_ACCOUNT_KEY -->|"以加密方式儲存"| ACCOUNT_KEY
ACCOUNT_KEY -->|"AES-256-GCM 解密"| DECRYPT
ENC_CREDS -->|"從資料庫讀取"| DECRYPT
三層保護:
| 層級 | 金鑰 | 保護方式 | 用途 |
|---|---|---|---|
| 第一層 | KMS 主金鑰 | AWS HSM 硬體(不可匯出) | 加密/解密每帳戶金鑰 |
| 第二層 | 每帳戶金鑰 | 以主金鑰加密,儲存於資料庫 | 加密/解密實際憑證 |
| 第三層 | 憑證密文 | 以每帳戶金鑰加密 | 靜態儲存的券商 API 金鑰和密鑰 |
為什麼需要三層?
如果資料庫被入侵,攻擊者取得第三層(密文)和第二層(加密的帳戶金鑰),但若無第一層(HSM 中的 KMS 主金鑰)則無法解密任何資料。如果應用程式記憶體被傾印,攻擊者取得一位使用者的已解密憑證,但無法取得其他使用者的——每個帳戶都有獨立的金鑰。
前端到後端加密¶
當使用者透過控制面板提交券商憑證時,混合 RSA-4096 + AES-256-GCM 加密方案提供端對端保護——即使 TLS 被中間人代理或 CDN 設定錯誤所攻破。
sequenceDiagram
participant Browser as 瀏覽器(Web Crypto API)
participant API as FastAPI
participant KMS as AWS KMS HSM
participant DB as PostgreSQL
Browser->>API: 1. GET /auth/encryption-key
API->>KMS: GetPublicKey(key_id)
KMS-->>API: RSA-4096 公鑰(PEM)
API-->>Browser: 公鑰(已快取)
Note over Browser: 2. 產生隨機 AES-256 金鑰<br/>crypto.subtle.generateKey("AES-GCM", 256)
Note over Browser: 3. 以 AES-GCM 加密憑證<br/>每次操作隨機產生 12 位元組 IV
Note over Browser: 4. 以 RSA-OAEP-256 加密 AES 金鑰<br/>crypto.subtle.encrypt("RSA-OAEP", publicKey, aesKey)
Browser->>API: 5. POST /trading/accounts<br/>{encrypted_key, encrypted_data, iv, tag}
API->>KMS: 6. Decrypt(encrypted_key)<br/>私鑰留在 HSM 中
KMS-->>API: 已解密的 AES-256 金鑰
API->>API: 7. AES-GCM 解密憑證<br/>使用已解密的 AES 金鑰 + IV + 標籤
API->>API: 8. 以每帳戶儲存金鑰<br/>重新加密(AES-256-GCM)
API->>DB: 9. 儲存加密憑證<br/>(Nonce + 密文 + GCM 標籤)
Note over API: AES 金鑰和明文憑證<br/>僅存於記憶體中,隨後被垃圾回收
此流程的關鍵安全特性:
- 前向保密 (Forward Secrecy) —— 每次憑證提交都會產生全新的隨機 AES-256 金鑰。攻擊一個請求的金鑰不會解密其他請求。
- HSM 支援的解密 —— RSA-4096 私鑰永遠不離開 KMS HSM。步驟 6 是一個 KMS API 呼叫;明文 AES 金鑰會返回給應用程式,但私鑰始終留在硬體中。
- Web Crypto API —— 瀏覽器使用原生
crypto.subtleAPI(非 JavaScript 函式庫),在安全上下文中執行,且能抵抗側通道攻擊 (Side-Channel Attack)。 - 傳輸過程中無明文 —— 即使 HTTPS 已加密連線,憑證酬載仍獨立加密。TLS 攔截代理只能看到密文。
儲存加密(每位使用者 AES-256-GCM)¶
當憑證到達伺服器後,會以每帳戶金鑰重新加密,儲存於 PostgreSQL 中。
| 屬性 | 值 |
|---|---|
| 演算法 | AES-256-GCM(認證加密) |
| 金鑰大小 | 每個交易帳戶 32 位元組(256 位元) |
| Nonce (IV) | 12 位元組(96 位元),每次加密操作隨機產生 |
| 認證 | GCM 標籤提供完整性 + 機密性 |
| 金鑰隔離 | 每個 TradingAccount 都有獨立的加密金鑰 |
| 金鑰儲存 | 帳戶金鑰以主金鑰加密,儲存在 encrypted_key 資料庫欄位 |
| 加密版本 | 版本 2(目前);版本 1(Fernet/AES-128-CBC)用於舊有資料 |
資料庫欄位格式:
┌──────────┬────────────────────────┬──────────┐
│ 12-byte │ Variable-length │ 16-byte │
│ Nonce │ Ciphertext │ GCM Tag │
└──────────┴────────────────────────┴──────────┘
Base64-encoded, stored as TEXT
Nonce 唯一性
每次加密操作都使用 os.urandom(12) 產生 12 位元組的隨機 Nonce。在 AES-GCM 中使用相同金鑰重複使用 Nonce 會完全破壞加密方案的安全性。隨機產生確保這不會發生(碰撞機率:每對 2^-48)。
主金鑰管理¶
保護每帳戶金鑰的主金鑰有兩種模式,根據 USE_AWS_KMS 環境變數自動選擇:
| 屬性 | 值 |
|---|---|
| 金鑰類型 | KMS 管理的 AES-256 對稱金鑰 |
| 信封加密 | KMS 加密每帳戶金鑰;應用程式使用明文資料金鑰 |
| HSM 支援 | FIPS 140-2 Level 3 硬體安全模組 |
| 稽核 | 所有金鑰操作記錄於 AWS CloudTrail |
| 輪替 | KMS 自動金鑰輪替(每年) |
| 金鑰 ID | AWS_KMS_KEY_ID 環境變數 |
| 區域 | AWS_REGION / AWS_DEFAULT_REGION(預設:ap-southeast-1) |
自動模式選擇
應用程式根據 USE_AWS_KMS=true|false 自動選擇 KMS 或本地模式。在開發和正式環境之間切換不需要修改程式碼——只需更改環境變數。
加密版本控管¶
4pass 支援兩個加密版本,並具備自動前向遷移機制:
| 版本 | 演算法 | 金鑰大小 | 狀態 | 引入時間 |
|---|---|---|---|---|
| v1 | Fernet (AES-128-CBC + HMAC-SHA256) | 128 位元 | 舊版 | 初始發佈 |
| v2 | AES-256-GCM | 256 位元 | 目前(預設) | 安全性升級 |
讀取時自動升級:
flowchart TB
READ["讀取加密憑證"] --> CHECK{"版本?"}
CHECK -->|v1| DECRYPT_V1["以 Fernet 解密"]
CHECK -->|v2| DECRYPT_V2["以 AES-256-GCM 解密"]
DECRYPT_V1 --> REENCRYPT["重新加密為 v2"]
REENCRYPT --> SAVE["儲存升級後的密文"]
DECRYPT_V2 --> USE["回傳明文"]
SAVE --> USE
- 現有 v1 資料在下次讀取時會被解密並透明地重新加密為 v2
- 新資料一律使用 v2 (AES-256-GCM)
- 維持向後相容性——兩個版本都可以無限期解密
- 版本位元組前置於密文前,為未來的演算法升級預留空間
安全特性總覽¶
| 特性 | 機制 | 保證 |
|---|---|---|
| 前向保密 | 每次前端請求使用獨立的 AES-256 金鑰 | 攻擊一次提交不會解密其他提交 |
| 每位使用者隔離 | 每個交易帳戶使用獨立的 32 位元組金鑰 | 攻擊一個帳戶金鑰只影響該帳戶 |
| HSM 支援 | RSA-4096 私鑰存放於 KMS FIPS 140-2 L3 硬體中 | 私鑰永遠不存在於軟體記憶體中 |
| 認證加密 | AES-256-GCM 搭配 128 位元認證標籤 | 竄改會在解密前被偵測並拒絕 |
| 靜態無明文 | 憑證在寫入資料庫前加密 | 資料庫傾印只會顯示密文 |
| 傳輸中無明文 | RSA+AES 混合加密加上 HTTPS | 雙層加密——TLS 失效也不會暴露憑證 |
| 稽核軌跡 | KMS 操作記錄於 CloudTrail | 所有解密事件可追蹤且可告警 |
| 金鑰輪替 | KMS 自動年度輪替 + 版本遷移 | 無停機時間的前向安全性 |
攻擊者需要什麼
要解密單一使用者的券商憑證,攻擊者必須同時擁有:
- 資料庫存取權限(取得加密的憑證和加密的帳戶金鑰)
- KMS Decrypt 權限(使用主金鑰解封帳戶金鑰)
- 特定的帳戶金鑰(使用 AES-256-GCM 解密憑證)
僅攻破其中任何一項都無法揭露任何資訊。