起點:一個錯誤的安全模型
v3.1 freemium 系統用的是純前端 Crockford 授權碼 —— 驗證邏輯全在瀏覽器, 任何人打開 DevTools 都能繞過。問題不是「前端被看到」,而是 沒有任何後端在驗證。
五個 Stage 的改造
| Stage | 內容 |
|---|---|
| 1 · Worker JWT | 建立 Cloudflare Worker,HS256 + WebCrypto;端點 /health、/license/issue、/license/validate |
| 2 · v3.2 模組化 | index.html -334 行,抽出 5 個 JS 模組,純前端 Crockford 換成 JWT |
| 3 · v3.2.1 KV-backed | 引入 KV namespace;/issue 要求 KV status='active' 才簽;/validate 每次查 KV;JWT 1 年→7 天 + 自動續期 |
| 4 · v3.3.0 Sandbox | 第二個 Sandbox Worker + KV;前端 ?sandbox=1 切換;PayPal SDK 動態載入 |
| 5 · v3.3.1 retry fix | 修正 webhook 時序:對「403 + currentStatus='pending'」也 retry |
最終驗證(Sandbox e2e)
訂閱 I-R2SFXX7U1SW2:建立 → ACTIVATED → KV active;
取消 → CANCELLED webhook → KV cancelled;4 秒後 /license/validate 回 401。
即時撤銷成立。
關鍵教訓
「開源 vs. 收費」是迷思。前端被看到不是問題,後端驗證才是。架構正確時,整個前端公開都沒事。
JWT 短期 + KV 撤銷 vs 純 JWT 長期:訂閱系統必須前者才能即時撤銷。
Sandbox 是必需的,不是 nice-to-have —— 台灣 PayPal P2P 限制讓真實 Live 自我測試不可能。
base64UrlEncode 對 binary buffer:要逐 byte 從 Uint8Array 構造,別把 ArrayBuffer 轉字串再 btoa(會被當 UTF-8 雙重編碼)。
來源:個人開發日誌 2026-05-14(v3.2 → v3.3.1)· 3 commits · 2 Worker · 2 KV namespace · 5 個攻擊向量已防禦