彙整自
security-review-checklist.md、security-architecture.md、media-and-attachment-security.md、security-assumptions-and-out-of-scope.md的所有掃描項目。
| # | 項目 | 來源 | 說明 |
|---|---|---|---|
security-review-checklist.md §4.3 |
✅ Phase 1 已修復:新增 account_token_hash 欄位,雙模式驗證(hash 優先 + 明文 fallback + 自動回填),新帳號直接寫 hash |
||
security-review-checklist.md §4.3 |
✅ 已修復:透過 ENABLE_DEBUG_ENDPOINTS 環境變數控制,生產環境預設 false 回傳 404(wrangler.toml、worker.js) |
||
security-review-checklist.md §4.3 |
⬇️ 降級為 Low:系統不使用 cookie 認證(token 透過 header 傳送),傳統 CSRF 不成立 | ||
| C-4 | Send-side ratchet 停用 | security-review-checklist.md §2.2, security-architecture.md §10 |
dr.js:357-364 中 send-side ratchet 更新被註解,myRatchetPriv/myRatchetPub 不在發送時輪替,削弱前向保密性 |
| # | 項目 | 來源 | 狀態 | 說明 |
|---|---|---|---|---|
security-architecture.md §10 |
✅ 已修復(H-2 升級) | Phase 1:抽取共用 jwt.js 模組。Phase 2(H-2 升級):全面遷移至 jose 套件(panva/jose,經審計、零依賴、Web Crypto 原生)。HS256(WS token)使用 SignJWT / jwtVerify(constant-time 驗證 + 嚴格 algorithms: ['HS256'])。RS256(Voucher token)使用 importSPKI + jwtVerify(嚴格 algorithms: ['RS256'] + exp 驗證 + 30 秒 clockTolerance)。同步修復:P0 verifyJwtRS256 未驗證 exp、P0 未檢查 alg(演算法混淆風險)、P1 簽章字串比對非 constant-time。移除 worker.js 中所有重複的手工 JWT helper。 |
||
security-architecture.md §10 |
✅ 已修復(H-2 強化) | Phase 1:移除 dr.js 中所有 hashPrefix() 輸出。Phase 2(H-2 強化):全面清理 debug 日誌安全問題 — (1) debug-flags.js 所有開關預設 false(不再依賴 !_PROD)(2) dr-session.js 移除金鑰存在狀態(hasRk/hasCkS/hasCkR)和計數器值(Ns/Nr)的日誌輸出 (3) 移除 navigator.webdriver 自動啟用詳細日誌(防止 Selenium 環境洩露)(4) hybrid-flow.js DEBUG.drVerbose 改為 false (5) entry-fetch.js FETCH_LOG_ENABLED 改為 false (6) worker.js logMessageKeyVault 僅輸出安全欄位 (7) contacts.js 移除 MK 狀態和聯絡人資料明文日誌 (8) login-flow.js deviceId 截斷為前 8 字元 (9) contact-secrets.js 移除 webdriver 自動啟用 |
||
security-assumptions-and-out-of-scope.md §6 |
✅ 已修復 | 三層防護:(1) ENABLE_DEBUG_PAGES 環境變數 → 生產回傳 404 (2) IP 白名單 (3) __PRODUCTION__ build flag 關閉 debug switches |
||
security-review-checklist.md §4.3 |
✅ 已修復 | 移除 Replay 回應的 lastCtr 和 CounterTooLow 回應的 maxCounter/details 欄位 |
||
media-and-attachment-security.md §7.2 |
⬇️ 降級為 Low | E2EE 零知識架構固有限制:媒體需支援跨裝置歷史下載,需持久金鑰;MK 洩漏前提需終端入侵或暴力破解 Argon2id |
| # | 項目 | 來源 | 說明 |
|---|---|---|---|
security-review-checklist.md §4.3 |
✅ 已修復:新增 RateLimiter Durable Object,提供跨 isolate 分散式限流;全域 IP 限流 (120/min) + 認證端點 (10/min) + prekey (20/min) + 訊息發送 (60/min) + pairing code (3/30s) |
||
security-review-checklist.md §5.1, media-and-attachment-security.md §7.2 |
⬇️ 降級為 Low:攻擊前提需同時擁有 MK + R2 存取權,chunk 重排僅影響單一媒體完整性,不洩漏新資料 | ||
security-review-checklist.md §5.2, security-architecture.md §10 |
⬇️ 降級為 Low:GCM auth tag 已提供完整性與認證保證,額外簽章需三重前提(MK 洩漏 + R2 存取 + DR 突破)才有價值 | ||
security-architecture.md §10 |
✅ 已修復:所有 AES-GCM 操作加入 additionalData(info tag / 用途標識),新資料使用 v:2 格式標記,解密時依版本判斷是否驗證 AAD(向下相容 v1 legacy) |
||
security-architecture.md §10 |
⬇️ 降級為 Low:大部分操作使用 per-object HKDF salt 衍生獨立金鑰,IV 碰撞不影響安全性(不同 key);少數固定 salt 操作(contact-share、contact-blob)碰撞機率 < 10⁻²⁰ | ||
security-architecture.md §10 |
✅ 已修復:改為每次 seal 產生 16-byte random salt,存入 envelope salt_b64;舊 envelope 向下相容(fallback 舊 salt) |
||
security-architecture.md §10 |
✅ 已修復:CMK 512-bit 輸出拆分為 key (256-bit) + subSalt (256-bit),子金鑰衍生使用 subSalt 取代零 salt | ||
security-review-checklist.md §6.2 |
✅ 已修復:caller 每 1 分鐘自動遞增 epoch 並重新衍生金鑰(key-manager.js),透過 call-rekey 信號傳送新 envelope 給 peer;createEncryptionTransform 動態偵測 epoch 變更並即時切換加密金鑰 |
||
security-review-checklist.md §6.2 |
✅ 已修復:本地或對端不支援 InsertableStreams 時拒絕建立通話(failCall),不允許靜默降級為未加密通話 |
||
security-review-checklist.md §9 |
✅ Phase 1 已修復:新增 CSP header,白名單限制 script-src ('self' + 'wasm-unsafe-eval' + 3 CDN + blob:)、禁止 frame/object、加入 X-Frame-Options: DENY 等安全 headers;Phase 2 待移除 'unsafe-inline' |
||
security-review-checklist.md §9 |
✅ 已修復:Pages Function 從 origin reflection 改為白名單比對(CORS_ALLOWED_ORIGINS);Data Worker 設定 CORS_ORIGINS 環境變數限制允許的 origin;明確列舉 allow-headers 取代 * |
||
security-review-checklist.md §9 |
✅ 已修復:與 M-1 共同處理,全域 IP 限流覆蓋所有 API 端點 |
| # | 項目 | 來源 | 說明 |
|---|---|---|---|
| L-1 | JavaScript GC 不保證立即清除金鑰 | security-review-checklist.md §3.3 |
記憶體中金鑰可能在 GC 週期內殘留 |
localStorage contactSecrets-v2 登出時是否清除 |
security-review-checklist.md §3.3 |
✅ 已確認/修復:secureLogout() 已有 localStorage.clear();app-ui.js 的 onLogout() 修正為清除所有非 SIM localStorage key(原僅清除 env_v1:*) |
|
| L-3 | 社交圖譜可見(持續緩解中) | security-review-checklist.md §7 |
conversation_acl 明文儲存,伺服器可見對話參與者。Phase 0-A:contacts 表 peer_digest → HMAC slot_id。Phase 0-B:role 不再寫入明文(NULL)、receiver_device_id 停止寫入、contacts.updated_at 截斷至每日精度。Phase 0-C:push_subscriptions.user_agent 改為短標籤,移除裝置指紋 |
| L-4 | 通訊時間可見 | security-review-checklist.md §7 |
Timestamp 明文,伺服器可見通訊時間 |
| L-5 | 訊息大小可推知 | security-review-checklist.md §7 |
無 padding 機制,訊息密文大小可推知明文長度 |
| L-6 | 在線狀態可推知 | security-review-checklist.md §7 |
WebSocket/presence 機制暴露使用者在線狀態 |
| L-7 | P2P 連線可能暴露 IP | security-review-checklist.md §6.3 |
ICE candidate 類型未限制為 relay only |
| L-8 | Chunk 大小洩漏明文大小 | media-and-attachment-security.md §7.2 |
GCM overhead 固定 16 bytes,密文大小可推知明文 |
| L-9 | 上傳時序可推知通訊行為 | media-and-attachment-security.md §7.2 |
R2 寫入時間與訊息時間可關聯 |
media-and-attachment-security.md §7.2 |
✅ 已確認:頭像使用 AES-256-GCM + HKDF(MK, random_salt) 加密後上傳 R2(.enc),下載後解密為 blob: URL 顯示,與一般媒體相同加密流程 |
||
| L-11 | Cloudflare 日誌是否記錄 API request body | security-review-checklist.md §11 |
第三方基礎設施可能保留請求內容 |
security-review-checklist.md §9 |
✅ 已修復:__PRODUCTION__ build flag 在生產環境關閉所有 debug switches |
||
| L-14 | 無 CSRF token 驗證(由 C-3 降級) | security-review-checklist.md §4.3 |
系統不使用 cookie 認證,傳統 CSRF 不成立;可選加 Origin header 驗證作為縱深防禦 |
| L-15 | MK 洩漏影響所有媒體(由 H-5 降級) | media-and-attachment-security.md §7.2 |
E2EE 架構固有限制:媒體需持久金鑰支援歷史下載,MK 洩漏前提需終端入侵或暴力破解 Argon2id |
| L-13 | D1/R2 資料殘留無 TTL 清理 | security-review-checklist.md §11 |
訊息和媒體無自動清理機制 |
security-review-checklist.md §5.1 |
✅ 已由 M-4 修復:所有 AES-GCM 操作已加入 AAD(info tag 作為 additionalData),v2 格式向下相容 v1 legacy |
||
| L-17 | Manifest 無獨立簽章(由 M-3 降級) | security-review-checklist.md §5.2 |
GCM auth tag 已提供完整性保證,額外簽章需三重前提才有實際價值 |
| L-18 | IV 重用風險(由 M-5 降級) | security-architecture.md §10 |
per-object HKDF salt 確保每次加密獨立金鑰,(key, IV) pair 不重複;固定 salt 操作碰撞機率 < 10⁻²⁰ |
| 嚴重程度 | 總數 | 已修復 | 降級 | 待處理 |
|---|---|---|---|---|
| 🔴 Critical | 4 | 2 | 1 (→Low) | 1 |
| 🟠 High | 5 | 5 | 1 (→Low) | 0 |
| 🟡 Medium | 12 | 9 | 3 (→Low) | 0 |
| 🟢 Low | 13+5 | 4 | — | 14 |
| 總計 | 36 | 20 | 5 | 13 |
以下為 checklist 中已確認通過的項目:
- ✅ TOFU:首次 X3DH 儲存 peer Identity Key,後續偵測 key 變更(
contact-secrets.js) - ✅ Safety Number:雙方可透過 60 位數字指紋帶外驗證身份(
safety-number.js) - ✅ Identity Key 變更時觸發
dr:identity-key-changed事件(dr-session.js) - ✅ WebSocket 訊息大小限制(Signal: 16KB, SDP: 64KB)
- ✅ Ephemeral buffer 限制(50 messages, 5 min TTL)
- ✅ SQL injection 防護 — 全部 358 處使用 parameterized queries
- ✅ 輸入正規化:account_digest、conversation_id 等
- ✅ 訊息 counter 嚴格遞增(server-side 檢查)
- ✅ GitHub Actions secrets 不透過 echo 傳入
- ✅ DR 狀態並發已有
enqueueDrSessionOp()序列化機制 - ✅ C-1:Account token hash 儲存(Phase 1:雙模式驗證 + 自動回填)
- ✅ C-2:Debug endpoints 透過
ENABLE_DEBUG_ENDPOINTS環境變數控制,生產環境回傳 404 - ✅ H-1:JWT 驗證全面遷移至
jose套件 — HS256 使用SignJWT/jwtVerify(constant-time + 嚴格 alg 白名單),RS256 使用importSPKI+jwtVerify(含exp驗證 + alg confusion 防護)。移除所有手工 JWT helper - ✅ H-2:全面清理 debug 日誌安全問題 —
debug-flags.js所有開關預設 false、dr-session.js移除金鑰狀態/計數器日誌、移除navigator.webdriver自動啟用、worker.jsvault 日誌僅輸出安全欄位、contacts.js/login-flow.js敏感資料截斷/移除 - ✅ H-3:Debug 頁面三層防護(env gate + IP 白名單 +
__PRODUCTION__build flag) - ✅ H-4:移除錯誤回應中的內部狀態欄位(
lastCtr、maxCounter) - ✅ M-6:Invite Dropbox 改用 per-envelope 16-byte random salt(向下相容舊 envelope)
- ✅ M-7:Call key 子金鑰衍生改用 CMK 拆分的 subSalt 取代零 salt
- ✅ M-9:不支援 InsertableStreams 時拒絕通話,防止靜默降級為未加密通話
- ✅ M-1/M-12:新增
RateLimiterDurable Object 分散式限流,覆蓋全域 IP、認證、prekey、訊息發送、pairing code - ✅ M-10:Phase 1 CSP — 白名單 script-src(含
'wasm-unsafe-eval'支援 Argon2 WASM)、禁止 frame/object、加入安全 headers(X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) - ✅ M-4:所有 AES-GCM 操作加入 AAD —
aead.js(info tag)、invite-dropbox.js、context.js、contact-share.js、kdf.js(sentry/mk-wrap)、contacts.js(contact-storage-v1);v2 格式向下相容 v1 legacy - ✅ M-8:Epoch 輪換實作 — caller 每 1 分鐘自動遞增 epoch 重新衍生金鑰,透過
call-rekey信號同步 peer,InsertableStreamstransform 動態切換加密金鑰 - ✅ M-11:CORS 白名單限制 — Pages Function 改用
CORS_ALLOWED_ORIGINS白名單取代 origin reflection;Data Worker 啟用CORS_ORIGINS環境變數;明確列舉 allow-headers - ✅ L-2:登出時清除所有 localStorage —
secureLogout()已有localStorage.clear(),app-ui.jsonLogout()修正為清除所有非 SIM key - ✅ L-10:頭像已確認使用 AES-256-GCM + HKDF(MK, random_salt) 加密上傳,與一般媒體相同加密流程
- ✅ L-12:Debug flags 在生產環境建置時強制關閉
- ✅ L-16:Media chunk AAD 已由 M-4 統一修復(所有 AES-GCM 加入 info tag 作為 additionalData)
- ✅ 密碼學原語函式庫:TweetNaCl + 手刻 ed2curve(~230 行 curve25519 有限域算術)已全面替換為
libsodium-wrappers-sumo(經 Cure53、Paragon Initiative 等多家安全公司審計)。Ed25519→X25519 轉換改用 libsodium 內建crypto_sign_ed25519_pk_to_curve25519(),libs/nacl-fast.min.js已刪除