FR-076 · 需求索引 · 本頁由 build 掃資料夾生成
用 Claude Code 的 claude-security plugin 掃 License 簽發與驗證鏈,找資安問題。 只掃不修——產出是問題清單,修正另外開卡。
產品的授權機制由兩端組成:license_center 負責簽照(私鑰在這裡), jedi-license-runtime 裝在主產品裡負責驗照。兩端從來沒有被系統性掃過。
這比 jedi-iam 更值得掃,因為攻擊者的動機明確且獲利直接:繞過授權等於免費使用整套產品。 而 jedi-iam 的漏洞多半要先有帳號或有網路位置。
涵蓋空白的證據:2026-07 的資安掃描(security-scan-2607)只掃主專案 BE 與 FE; FR-075 掃的是 jedi-common 與 jedi-iam。License 鏈完全沒被碰過。
第一個對外版本是落地版(Host),SaaS 版機制要預留但不是第一版。裁決修正優先序時用這個判準: 只影響 SaaS 的問題(停權、多租戶競態)綁 SaaS 上線前;落地版也打得到的問題(驗章、解壓炸彈、 機器指紋)綁落地版出貨前。落地版一台機器一張照綁頂層客戶(D8),子租戶不另發照。
verify_payload 同收 v2(format_version: 2)與 v1(明文 dict), 兩條路徑的檢查是否等價、能否降級攻擊payload_type 缺鍵時預設當 license——型別混淆的空間/etc/machine-id 就用 uuid.getnode(), 攻擊者能否讓它變成可控值(FR-064 曾發生容器空檔導致指紋漂移)| 棒 | 範圍 | repo | 檔數 | 卡號 | 狀態 |
|---|---|---|---|---|---|
| L1 | License 驗章核心(密碼學層) | jedi-license-runtime | 10 | CM-1567 | ✅ 已完成 — 1 HIGH(範圍內) |
| L2 | License 授權狀態與 API | jedi-license-runtime | 33 | CM-1568 | ✅ 已完成 — 1 LOW(範圍內)+1 HIGH(人工發現) |
| L3 | License Center 簽發核心與模組 | license_center | 32 | CM-1569 | ✅ 已完成 — 1 HIGH+1 MEDIUM(範圍內) |
| L4 | License Center 網頁後台 | license_center | 43 | CM-1571 | ✅ 已完成 — 4 MEDIUM(範圍內) |
L1 已完成(2026-09-06):55 分鐘一次跑完、零 stalled,面板 verified。 範圍內找到 1 條 HIGH——驗章前的無界解壓縮,任何登入者用 1.4MB 請求可讓後端配置 1GB 記憶體、 打掛 API(落地版單容器等於全產品停擺)。詳見 scan-L1-verification-core.md。
「Opus 1M + 小範圍」組合驗證成立:對照 FR-075 用 Sonnet 跑 40 檔以上四棒全滅共燒 29.6 小時, L1 用 Opus 1M 掃 10 檔 55 分鐘一次跑完。L2~L4 照此設定跑,可平行。
L2 已完成(2026-09-06):33 檔 90 分鐘一次跑完、零 stalled,面板 verified。 掃描在範圍內只找到 1 條 LOW(跨租戶重複用照的競態),另 1 條是 L1 已報過的同一個洞(越界)。 但人工復核時另外發現 1 條 HIGH:被停權的租戶只要自己上傳一張舊照,唯讀就解除—— 停權旗標寫在照的列上,而換照會建新列且不繼承。詳見 scan-L2-license-state-api.md。
L4 已完成(2026-09-06):43 檔(四棒最大)110 分鐘一次跑完、零 stalled,面板 verified。 範圍內找到 4 條 MEDIUM,無 HIGH,全部集中在兩處:線上開通端點(/api/activation/activate, 唯一免認證的對外入口)與登入導轉。開通序號只有 32 bits 且防猜鎖定「用被猜的序號當 key」等於沒鎖, 可暴力猜到別人的照;防猜用的記憶體結構無上限且查詢時也寫入,免認證即可撐爆 worker; 登入後的 next 未驗證可用來釣管理密碼。同 repo 的後台登入早就做對了(存 DB、計 IP、 docstring 還寫明為什麼不能存記憶體),開通端點沒套用——是遺漏不是不懂。 詳見 scan-L4-web-backoffice.md。
L3 已完成(2026-09-06):32 檔 129 分鐘一次跑完(1 次 agent stall 自動重試成功),面板 verified。 範圍內找到 1 HIGH + 1 MEDIUM,兩條是同一件事的兩面:開通序號只有 8 個十六進位字(32 bits), 而專門「把序號遮起來再寫 log」的那支函式遮罩取前 8 碼——序號剛好 8 碼,等於整串印進 journal。 兩處各自的假設都合理(logging 假設「前綴太短猜不出」、licensing 假設「夠亂不必更長」), 但沒有任何一處把兩個假設對起來。守門測試因為餵 18 字元 fixture,這個洞一直是綠燈。 另 3 條是研究員讀出範圍外的 web/ 層,與 L4 報過的完全對應(兩棒獨立掃描給出一致判定, 算是一次交叉驗證),不重複開卡。詳見 scan-L3-issuance-core.md。
L3 順帶釐清一件事:L1 判 HIGH 的解壓炸彈(CM-1572)在 LC 這一側被面板 0-3 否決, 兩邊不衝突——LC 沒有任何 HTTP 端點會收外部照檔(verify_payload 只被本機 CLI 與 「讀自己 DB 自己簽的照」呼叫),沒有攻擊者可控輸入源。但 engine.py 兩邊同源, CM-1572 修的時候要確認兩邊都修到。
| 卡號 | 修什麼 | 來源 | 嚴重度 | 狀態 |
|---|---|---|---|---|
| CM-1572 | 驗章前無界解壓縮:有界解壓+長度預檢+host MAX_CONTENT_LENGTH,含 jedi-integrity 兩處同款 | L1 F1,面板 3/3 | HIGH | ✅ ①② 驗收通過(套件 7091e04,首腦跑 62+90 測試綠);③ MAX_CONTENT_LENGTH 待決策者裁值 |
| CM-1573 | jedi-issue PAT 外洩處置:確認撤銷、清 Nexus 舊版與 dist、pyproject 排除 .env | L1 越界 F2,面板 2/3 | MEDIUM | ⬜ 待派 |
| CM-1579 | 停權可被租戶換照解除:方案 A 定案(停權移租戶層),SaaS 上線前修,不擋落地版 | L2 人工發現,工具未報 | HIGH(落地版低、SaaS 高) | ⏸ 暫緩至 SaaS 前 |
| CM-1580 | 跨租戶重複用照 TOCTOU:license_id 部分唯一索引(僅 is_current)+接 IntegrityError |
L2 F2,面板通過 | LOW | ✅ 驗收通過(套件 6cf1879+BE f3c58696,DEV 索引已建首腦查過;STG/POC 等令) |
| CM-1581 | build_bundle.sh 公鑰守門路徑失效:FR-069 搬遷後檢查的檔不存在,改用 python 解析套件路徑 | 首腦復核 L1 公鑰議題時發現 | 小 fix | ✅ 已修(4b527704),首腦 dry-run 驗過 |
| CM-1583 | LC 開通端點防猜失效+計數表無上限+log 遮蔽函式整串印出+補發復活舊序號:七項 | L4 F2+F3+F4 + L3 F1+F2 + L3 runner 追出,面板各 3/3 | MEDIUM→HIGH(log 洩憑證),客戶 BE 打得到 | ⬜ 待派,第 ⑦ 項先問補發語意 |
| CM-1584 | LC 登入 next 參數開放導轉:urlsplit 擋外站 | L4 F1,面板 3/3 | MEDIUM | ⬜ 待派 |
| (建議併入 CM-1583) | 開通序號熵只有 32 bits + 遮蔽函式整串印出:secrets.token_hex(16)、未兌換序號加到期、activation_code_digest 改印 keyed hash 或 license_id、補真實長度守門測試 |
L3 F1+F2,面板各 3/3 | HIGH(F1)+MEDIUM(F2) | ⬜ 待首腦裁定併卡或另開 |
| CM-1582 | 客戶包公鑰白名單:出包按環境只編 PROD 鑰、DEV 鑰不進客戶包 | L1 三環境硬編公鑰議題 | 綁正式 LC | ⏸ 暫緩至正式 LC |
三環境硬編公鑰的裁決(決策者 2026-09-06):現階段 POC、無外部客戶、三把鑰都在自己機器上,不急; 綁正式 LC 建立時一併處理。但首腦查到 build 守門本身壞了(檢查的路徑在 FR-069 搬遷後不存在, 現在每次出包都帶 --skip-prod-key-check 所以沒發現),這是小 fix 現在就修(CM-1581)。
L2 兩條的處置:掃描報的 F1 越界(common/engine.py 屬 L1 範圍)且與 L1 F1 同一條, 已由 CM-1572 涵蓋,不重複開卡。F2 在範圍內但掃描建議的全表唯一索引會擋掉正當的「換回舊照」流程, 報告內已改為部分唯一索引。人工發現的停權繞過建議優先於掃描報出的兩條——它不會被下一輪掃出來。
L1 三條發現的處置:F1 在範圍內開 CM-1572。F2 越界(jedi-issue)但屬憑證外洩,首腦用 unzip -l 確認 .env 仍在 0.0.14/0.0.15 wheel 內,獨立開 CM-1573。F3 越界(jedi-iam Turnstile)是第三次被獨立確認, 併入 FR-075 既有的 CM-1558 不另開。
首腦裁決待辦:三環境硬編公鑰(DEV 私鑰簽的照 POC 也認)——研究員讀過 public_keys.py 沒報, 可能判定為刻意設計。這在程式碼裡是明擺著的事實,由決策者直接裁定是設計還是疏漏,不等下一輪掃描。
「Opus 1M + 小範圍」組合驗證成立,L2/L3/L4 照同設定跑(三棒之間無衝突,可平行)。 但要注意研究員再次越界讀檔(與 FR-075 S7 同一模式):3 條面板存活發現裡只有 1 條在指派範圍內, 另 2 條分別指向 jedi-issue 與 jedi-iam。面板只驗「發現成不成立」、不驗「是否在範圍內」, 收報告時必須自己挑掉範圍外的。
effort: xhigh,200K 撐不住就會被 workflow 的 180 秒 stall 門檻砍掉重派。verification 欄確認面板真的跑完,不要只信研究員自述license_center/keys/private.pem 是正式簽發私鑰,在工作目錄內、掃描 agent 讀得到 (已確認未入版控)。任何情況下都不可把私鑰內容、passphrase 或金鑰材料寫進報告、 commit、Notion 或任何落地檔案。 報告指涉金鑰時只寫路徑與行為描述,不寫值。 L3/L4 兩張子卡內有同樣的警語。
scan-methodology.md以下全部由 build 掃資料夾產生,新增檔案重 build 即自動出現。標題連結指向渲染後的 HTML,md 連向源檔。
| 文件 | 類型 | 標題 | 最後更新 |
|---|---|---|---|
| scan-L1-verification-core / md | 文件 | L1 掃描結果:License 驗章核心(密碼學層) | 2026-09-06 |
| scan-L2-license-state-api / md | 文件 | L2 掃描結果:License 授權狀態與 API | 2026-09-06 |
| scan-L3-issuance-core / md | 文件 | L3 掃描結果:License Center 簽發核心與模組 | 2026-09-06 |
| scan-L4-web-backoffice / md | 文件 | L4 掃描結果:License Center 網頁後台 | 2026-09-06 |
由新到舊。每份是某一棒次交接當下的完整現況快照,看某個時間點「當時知道什麼」請從這裡進。