↑ 本站首頁

Guidant AI 資安檢視總報告 · STRIDE 威脅分類

D 讓服務停擺(Denial of Service)

命中這一類的 34 件,還原成 34 條原始條目(32 條出自模組頁,2 條只在總表、沒有模組頁逐條表)。每一條用白話寫出:在哪裡、攻擊面、信任邊界、元件端點、駭客怎麼打、得手什麼、修正的做法。

🟠 3 🟡 19 ⚪ 12 已修 33/不修 1
§1

這一類是什麼

讓正常使用者用不了服務。STRIDE 六類裡它破壞的是「可用性」這個性質——資料沒外洩、身分也沒被冒充,但產品變慢、卡住或整個不回應。依微軟的定義:拒絕或降低對合法使用者的服務,例如讓網站暫時無法使用。

判定時問的是:攻擊者打下這一條之後,能不能讓產品對所有人變慢或停擺? 這一類在本產品特別容易成立,原因是一個共同的前提:落地版預設只有 4 條處理程序、每條一次處理一個請求、120 秒才強制中止,而且多數檔案解析是在請求當下同步做完的。 所以只要一個請求能佔住一條處理程序幾十秒,四個同時送就讓所有客戶一起連不上。

這次的 34 條可以歸成四種形狀:

  • 小檔案解開變巨物(M18-2、M03-3、M03-4、M11-23、M11-24、M12-2、M12-3、M06-23、M11-34、M11-36、M07-8):系統只量上傳檔多大、不量解開後多大。授權檔、規則包、Excel、Word、PDF、YAML 範本,同一個病出現在十一個入口——幾 MB 的檔解開成幾 GB,記憶體吃光、處理程序被砍。
  • 特製文字讓比對越跑越慢(M04-15、M11-25、M11-26、M11-35、M11-37、M12-8):程式用來比對文字的規則遇到刻意設計的內容,耗時隨長度平方甚至更快成長。最嚴重的 M04-15 不必登入——系統在檢查身分之前就先拿請求內容去遮密碼。
  • 該有上限的量沒有上限(M06-3、M06-4、M04-7、M19-3、M14-1、M03-10、M03-11):一頁幾筆、聊天訊息多長、同時開幾條背景工作、一個網段展開幾台、流程圖分岔幾層——都由呼叫者說了算。
  • 一個小錯誤讓整條路停擺(M06-8、總表#103、M20-4、M21-3、M02-12、總表#145、M24-16、M24-17、M04-9、M24-15):一筆壞資料讓所有人的清單頁一直出錯、寫日誌失敗把使用者的請求一起弄壞、刪檔順序反了讓原廠範本全部打不開。這一組多半不是駭客能挑的入口,而是正常操作就會踩到的「功能自己停擺」。

修法也有共同的形狀:檢查要放在第一次打開檔案之前(只讀壓縮檔目錄、不解壓)、比對規則改成不會回頭重試的寫法並先截長度、每一種「量」都給一個寬鬆但不是無限的上限——上限抓太小頂多有人回報功能不夠用,看得到、改得掉;不設上限則是全產品停擺,看不到、來不及救。

STRIDE 與 OWASP 的關係:OWASP 講「程式哪裡寫錯」,STRIDE 講「攻擊者能拿它做什麼」。同一條問題通常兩邊都命中,所以這一頁的條目會與 OWASP 各頁重複出現,內容相同——兩邊各自完整,讀者從哪一邊進來都看得完。

兩條沒有模組頁出處:總表#103(寫日誌失敗連累請求)與總表#145(公告空值崩潰)來自掃描總表的「非資安但確實是錯誤」清單,模組頁沒有逐條表,本頁依總表描述、原始掃描報告與修正 commit 還原。

§2

條目清單

# 條目 嚴重度 出自 狀態
1 M06-3 一張特製的流程圖讓伺服器永遠算不完 🟠 高 稽核流程 ✅ 已修
2 M18-2 授權檔驗章之前就先解壓縮,而且沒有上限 🟠 高 授權管理 ✅ 已修
3 M04-15 不必登入,送一個特製內容的請求就讓整個產品停止回應 🟠 高 共用基礎 ✅ 已修
4 M06-4 「檢查流程圖」任何登入者都能打,而且不限大小 🟡 中 稽核流程 ✅ 已修
5 M21-3 沒有部門的一般使用者送意見回饋必定失敗、畫面不給理由 🟡 中 意見回饋重新檢視 ✅ 已修
6 M03-3 網址型規則包完全繞過壓縮檔的三道上限 🟡 中 弱點檢測整合 ✅ 已修
7 M03-4 規則包「最多一萬個檔」的上限對 zip 形同虛設 🟡 中 弱點檢測整合 ✅ 已修
8 M03-10 手動重新解析無條件開一條背景工作,可以把主機打掛 🟡 中 弱點檢測整合 ✅ 已修
9 M03-11 換掃描工具時舊的超大網段原封不動跟過去 🟡 中 弱點檢測整合 ✅ 已修
10 M04-7 一次要求回傳幾筆資料,沒有上限 🟡 中 共用基礎 ✅ 已修
11 M06-8 一筆空白內容的流程範本讓所有人的清單頁一直出錯 🟡 中 稽核流程 ✅ 已修
12 M14-1 聊天沒有長度上限,也沒有次數限制 🟡 中 AI 聊天助手 ✅ 已修
13 總表#103 寫日誌進資料庫失敗會連帶弄壞使用者的請求 🟡 中 總表 ✅ 已修
14 M20-4 兩支半夜清理程式靠「找不到身分就給最高權限」才跑得動 🟡 中 客戶資料隔離 ✅ 已修
15 M11-23 系統安全計畫的 Excel 匯入只量上傳檔、不量解開後多大 🟡 中 合規文件核心 ✅ 已修
16 M11-24 系統安全計畫的 Word 匯入同樣只量上傳檔大小 🟡 中 合規文件核心 ✅ 已修
17 M11-25 Excel「說明」頁填一百萬個數字,版本號比對就卡住 🟡 中 合規文件核心 ✅ 已修
18 M11-26 一份幾 KB 的特製 Word 讓系統安全計畫匯入卡到逾時 🟡 中 合規文件核心 ✅ 已修
19 M12-2 稽核計畫 Word 匯入的壓縮炸彈 🟡 中 稽核輪次管理 ✅ 已修
20 M12-3 稽核紀錄 Excel 在很後面填一格,系統就從頭一列列讀到那裡 🟡 中 稽核輪次管理 ✅ 已修
21 M06-23 匯入任務的 Excel 沒有上傳檢查、整份攤開在記憶體 🟡 中 稽核流程 ✅ 已修
22 M02-12 刪原廠共用檔時實體先被刪、紀錄卻刪不掉 🟡 中 檔案上傳下載 ✅ 已修
23 M04-9 兩種運作紀錄的詳細程度被寫死成最詳細 ⚪ 低 共用基礎 ✅ 已修
24 M07-8 分類程式沒有記憶體與處理器上限,逾時也殺不掉 ⚪ 低 證據自動分類 ✅ 已修
25 M19-3 設備清單一頁要顯示幾筆,沒有上限 ⚪ 低 設備與資訊系統清冊 ✅ 已修
26 總表#145 公告查無此筆對空值設值、沒有登入身分時建立公告崩潰 ⚪ 低 總表 ✅ 已修
27 M11-34 幾 KB 的特製 YAML 範本讓伺服器展開到當掉 ⚪ 低 合規文件核心 ✅ 已修
28 M11-35 範本匯入檢核送一格很長的怪字串,佔住處理程序兩分鐘 ⚪ 低 合規文件核心 ✅ 已修
29 M11-36 頁數極多的 CMMC PDF 讓解析時記憶體撐爆 ⚪ 低 合規文件核心 ✅ 已修
30 M11-37 PDF 裡一行超長的字讓「是不是目錄」的比對卡住 ⚪ 低 合規文件核心 ✅ 已修
31 M12-8 稽核計畫 Word 塞幾萬個空白,解析比對卡到逾時 ⚪ 低 稽核輪次管理 ✅ 已修
32 M24-16 首次安裝精靈設定碼塞中文或特殊字元,伺服器回 500 ⚪ 低 主系統自己的程式 ✅ 已修
33 M24-15 雲端硬碟通知說有去重複、實際沒做 ⚪ 低 主系統自己的程式 🚫 裁定不修
34 M24-17 匯出診斷包時一邊帶時區一邊不帶,程式當掉回 500 ⚪ 低 主系統自己的程式 ✅ 已修

§3

M06-3 🟠 一張特製的流程圖讓伺服器永遠算不完

欄位 內容
嚴重度 🟠 高(總表 #16;OWASP:A06 不安全的設計)
在哪裡 稽核流程模組:系統推算「下一步輪到誰」的那段程式——打開稽核輪次頁面、完成或退回任務、啟動一輪稽核時都會跑
攻擊面位置 需要登入且有「流程範本新增/修改」權限,把一張特製的流程圖存進去。存進去之後攻擊者什麼都不用做——任何看得到那個專案的人打開稽核輪次頁面,就替他觸發了。也不一定要有人存心:流程畫得太複雜的客戶可能無意間做出同樣的效果
信任邊界位置 【連線 C02:瀏覽器經前門打 api;打開輪次頁、完成任務時 api 推算流程】
使用者畫的流程圖 ↔︎ 伺服器的推算程式。流程圖是使用者提供的資料,推算程式應該把它當不可信輸入,走訪時記住走過哪裡、設深度上限;當時兩支函式互相呼叫、不記走過的連線,每多一層分岔下游就整段重算一次,工作量一層層相乘。而存檔時的兩道檢查擋的是「繞回自己的圈」與「分岔沒寫條件」,這張圖兩道都過得了
元件端點位置 推算核心在套件 jedi_flow_engine/common/utils/bpmn_uilts.py:get_next_jobs() 與 _get_jobs_from_exclusive_gateway() 互相呼叫。觸發入口:主專案 GET /project/{project_uid}/audit-round/{round_uid}/stage/info(StageInfoResource → app/flow_engine/service/stage_advance_service.py 的 _peek_next_stage_code_via_jobs())、/stage/advance、/flow-engine/task/complete/{id}、/flow-engine/task/revert/{id}(app/flow_engine/service/workflow_execution_service.py)。存進去的入口:POST /flow-engine/flow-templates、PUT /flow-engine/flow-templates/{uid}(flow_template_app_service.create()/update())
駭客怎麼打 ① 一個有流程範本編輯權限的帳號,畫一張「一層接一層連續分岔」的流程圖——不需要繞回自己,是一張完全合法的圖;② 存檔,新增與修改當時都不跑檢查,就算跑也過得了;③ 把這份範本用在某個專案的稽核輪次;④ 任何成員打開那一輪的頁面,畫面自動問「下一步輪到誰」,伺服器開始推算——10 層 2 秒、11 層 9 秒、12 層算不完,不報錯也不留紀錄;⑤ 四個人同時打開(或攻擊者自己開四個分頁),4 條處理程序全卡住,整個產品對所有客戶停止回應
得手什麼 讓整個產品對所有客戶停止回應,而且每次有人開頁面就重新觸發
修正的做法 ① 走訪記住走過哪裡:兩支函式之間傳「已走過的連線」與目前深度,走過的不再展開;深度超過 8 層或已走訪超過 100 條就記一筆警告後停止,上限值與主專案寫入端同一組,避免「存得進去卻走不完」;② 消掉指數成本:原本取預設分支時把同一個分岔點再算一遍,每過一個分岔點工作量翻倍,改成算一次傳下去;③ 主專案範本的新增與修改也跑檢查:補上原本只有發布才跑的兩支驗證,節點數/分岔深度上限擴及所有寫入路徑;④ 另擋「節點連回自己」,但不擋所有環——正常的送審退回本來就走回上一個節點,出貨內建範本就有這種迴圈
狀態 ✅ 已修(CM-2059,套件 commit 4a416ece、主專案 commit 8edbaf944,1.21.0 出貨)。驗證:攻擊圖修前永不返回、修後 0.001 秒返回並被寫入端擋下;DEV 27 份範本修前修後走訪結果逐份一致、0 份被新上限擋到。模組頁修法第⑤件「啟動輪次要求範本已發布」另由 CM-2083 補上(套件 commit d3cc1bc8:起輪次三條取範本路徑的匯合處檢查範本狀態,未發布一律擋下)
§4

M18-2 🟠 授權檔驗章之前就先解壓縮,而且沒有上限

欄位 內容
嚴重度 🟠 高(總表 #34;OWASP:A06 不安全的設計)
在哪裡 授權管理模組:離線開通——把原廠回寄的授權檔上傳進來
攻擊面位置 任何一個登入帳號都打得到:這支是刻意設計成「任何登入者可用」(授權過期的客戶唯一自救路徑),而且可以無限重複送
信任邊界位置 【連線 C02:瀏覽器經前門打 api;上傳授權檔,驗章前先解壓】
使用者上傳的檔案 ↔︎ 驗章程式。授權檔要先解壓才能驗章(簽章簽的是解開後的原文,順序不能對調),所以「還沒驗證過的資料」會先進到解壓這一步;這一步應該邊解邊設上限,當時完全沒有上限,在簽章來得及發揮保護之前記憶體就吃光了
元件端點位置 套件 jedi_license_runtime/api/routing.py:POST /license/activation/upload(LicenseActivationUploadRoute,AUTH_LOGIN)→ jedi_license_runtime/common/engine.py 的 _unpack_payload();上限常數在 common/constant.py
駭客怎麼打 ① 任何一個登入帳號,不需要特殊權限;② 準備一個「1GB 全零」的假內容,壓縮後約 1MB、編碼後約 1.4MB,包成授權檔格式;③ 從開通頁上傳;④ 系統在驗章之前先整份解開,處理程序記憶體吃到 1GB;⑤ 連送幾次,落地版是單一容器,整個產品被系統砍掉
得手什麼 用一個 1.4MB 的請求把整個產品打掛
修正的做法 ① 解碼之前先擋長度:編碼字串超過 32 MiB 連解碼都不做;② 解壓時有界:改用可設上限的解壓方式,最多解出 16 MiB,還有沒解完的就判超限拒絕——上限是實測最大正常授權檔的約 18 倍;③ 程式旁邊寫明「兩道都不可省、順序不可對調」的理由;④ 另兩處同款寫法(防竄改套件的測試工具與測試設定)一併改成有界,避免日後照抄舊寫法
狀態 ✅ 已修(CM-1572,套件 commit 7091e046)。總表狀態欄未帶卡號,修正依據為依「解壓炸彈」搜尋套件 commit 找到。驗證:新增 4 條測試(含記憶體量測),突變拿掉修法兩條轉紅
§5

M04-15 🟠 不必登入,送一個特製內容的請求就讓整個產品停止回應

欄位 內容
嚴重度 🟠 高(總表 #149;OWASP:A06 不安全的設計)。全案唯一不必登入就能讓產品停擺的一條
在哪裡 共用基礎:每個請求進來時,系統先把請求內容「遮密碼」再記進操作日誌——所有網址都經過這一段,包含不存在的網址
攻擊面位置 網路上任何人,不需要帳號。這一段跑在檢查身分之前,所以連登入都不必,打一個不存在的網址也會中
信任邊界位置 【連線 C01、C02:匿名請求經前門進 api,入口先對整份請求遮密碼】
匿名網路 ↔︎ 後端入口。在還不知道對方是誰之前,系統應該只做便宜、有上限的事;當時卻把整份請求內容丟進一段比對規則去遮密碼,而且一個請求遮兩遍。那段規則把欄位名寫成「前後各一段可任意長、夾著關鍵字」,遇到特定形狀的內容耗時隨長度平方成長
元件端點位置 任何路徑(例如不存在的網址)。主專案 common/middleware/app_mw.py:init_app_interceptor() 的 before_request → _loggable_body() → 套件 jedi-common/jedi_common/utils/common_utils.py 的 mark_password()
駭客怎麼打 ① 網路上任何人,不需要帳號;② 對我們的網站打一個不存在的網址,請求內容放 128KB 的特製文字(同一小段重複很多次);③ 系統還沒檢查身分就先拿它去遮密碼,比對卡住 6 秒以上(16KB 就要近 3 分鐘的形狀也存在);④ 同時送幾個,4 條處理程序全卡住,所有客戶一起連不上
得手什麼 不必任何帳號,讓整個產品對所有客戶停止回應
修正的做法 兩層都修:① 根因(套件):遮密碼規則改成一次比對一組「欄位名:值」,所有重複次數改成「吃了不吐」的寫法、欄位名加 256 字上限,是不是機密改在比對完之後另外判斷,耗時變成隨長度線性;值沒收尾(內容被截斷)時把後半視為值、照樣遮;② 入口(主專案):遮之前先把內容截到 64KB 並標「已截斷,原長 N bytes」,記日誌與寫資料庫共用同一份,一個請求只遮一次;③ 網址參數也改用同一張關鍵字表遮(mask_query_string)
狀態 ✅ 已修(FR-114 CM-2171,套件 jedi-common commit cc61f18e、主專案 commit 908f9dd0e,1.21.0 出貨;同卡另一支 jedi-iam commit 960d737e 修的是來源 IP,與本條無關)。驗證:不登入打不存在網址帶 16~128KB 攻擊內容,修後 0.02~0.04 秒回 404;新增 64KB 三種攻擊形狀須 0.5 秒內的效能測試,換回舊規則即轉紅
§6

M06-4 🟡 「檢查流程圖」任何登入者都能打,而且不限大小

欄位 內容
嚴重度 🟡 中(總表 #40;OWASP:A01 存取控制失效、A06 不安全的設計)
在哪裡 稽核流程模組:流程圖編輯器即時檢查畫得對不對的那支功能
攻擊面位置 任何一個登入帳號:同一個檔案裡其他功能都掛了權限檢查,只有這一支沒有;送進來的內容也不限長度、節點數與連線數
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者 ↔︎ 流程範本服務。該檢查的是「你有沒有編輯流程範本的權限」與「送進來的東西多大」,兩道都沒有。原本報告寫「打幾次就讓全站停擺」,實測後確認不成立(這支走的是另一套檢查邏輯,一千個節點 0.027 秒),實質影響是「沒權限的人可以用它、而且可以丟很大的東西進來」
元件端點位置 主專案 api/flow_engine/__init__.py:POST /flow-engine/flow-templates/validate(FlowTemplateValidateRoute.post,api/flow_engine/routes/flow_template_route.py)→ app/flow_engine/service/flow_template_app_service.py 的 validate();請求格式 api/flow_engine/serializers/flow_template.py 的 FlowTemplateValidateRequestSchema
駭客怎麼打 ① 任何一個登入帳號,即使沒有流程範本權限;② 直接打這支檢查功能,送一份很大的流程圖內容;③ 系統不問權限、不限大小,照收照算;④ 單獨打不到停擺,但這是一扇沒有任何關卡的門
得手什麼 沒有權限的人能使用流程範本功能,並能丟很大的內容進來消耗資源
修正的做法 ① 路由補上「流程範本修改」權限檢查(flow_template.update),與同檔其他寫入功能同級;② 請求格式加長度上限 35 萬字(DEV 最大範本約 14.7KB,等比放大到 100 節點再乘 3 倍);③ 服務層新增 _check_size_limits():節點數上限 100、分岔深度上限 8,走訪時遇到環直接停;④ 超限時維持原本「200+警告清單」的回應方式,不改成錯誤——前端編輯器的即時警告靠這個畫;⑤ 之後 CM-2059 把同一組上限擴到範本新增、修改、發布所有寫入路徑
狀態 ✅ 已修(FR-114.1-8/CM-2031,主專案 commit aa04ca1a7;上限擴及寫入路徑見 CM-2059 8edbaf944)。驗證:150 節點與 10 層分岔各回對應警告且仍是 200;35 萬零 1 字被擋
§7

M21-3 🟡 沒有部門的一般使用者送意見回饋必定失敗、畫面不給理由

欄位 內容
嚴重度 🟡 中(總表 #44;OWASP:A10 例外狀況處理不當)
在哪裡 意見回饋模組:使用者在「意見回饋」頁按送出
攻擊面位置 不是攻擊面,是功能自己壞掉:任何一個還沒被分配部門的正常帳號都會踩到,新客戶剛裝好、部門還沒建起來時特別容易
信任邊界位置 【連線 C05、C02:送出意見回饋,資料庫新增規則擋下卻沒翻成白話】
應用程式 ↔︎ 資料庫隔離規則。資料庫那層的「新增」規則多要求了「送出者必須有部門」,跟另外三條(查、改、刪)形狀不同;規則擋下時應用程式沒有把原因翻成白話,畫面只看到失敗
元件端點位置 jedi_issue/api/routing.py:POST /feedback(FeedbackRoute.post)→ feedback_service.add_feedback();擋下它的是資料庫規則 feedback_issues_insert,定義在 jedi-issue/jedi_issue/migrations/004-feedback-issues-rls-grants.sql(既有庫由主專案 scripts/sql/2026-09-22-fr114-feedback-issues-insert-relax-org.sql 換掉)
駭客怎麼打 ①(不是駭客,是一般使用者)管理員剛建好帳號、還沒指定部門;② 這個人打開意見回饋、填好內容按送出;③ 資料庫的新增規則看到「部門是空的」就擋下,畫面只顯示失敗;④ 管理員去查這個人的權限,每一項都對,查不出原因
得手什麼 沒有人得手;是正常使用者用不了功能,而且沒人看得出為什麼
修正的做法 ① 決策者裁定放寬規則:送意見回饋本來就與部門無關,不採「規定帳號必有部門」(影響所有建帳流程)也不採「只把錯誤講清楚」(沒解決卡住);② 「新增」規則改成跟另外三條一模一樣:超級管理員可略過,否則只看「這筆屬於哪一家客戶、你是不是那家的」,「必須有部門」整個拿掉;③ 套件裡給新裝庫用的那份與主專案給既有庫換規則的 migration 兩處同時改,少一邊就會「新裝的沒事、舊庫還卡著」;④ 客戶隔離沒跟著鬆:客戶欄位留空的列仍插不進去
狀態 ✅ 已修(FR-114.1-9/CM-2032,主專案 commit fc9fca7d0、套件 commit e98fea29)。驗證:沒部門的人送得出去且客戶欄位正確;跨客戶送、客戶欄位留空仍被擋
§8

M03-3 🟡 網址型規則包完全繞過壓縮檔的三道上限

欄位 內容
嚴重度 🟡 中(總表 #76;OWASP:A06 不安全的設計)
在哪裡 弱點檢測整合模組:建立檢測基準(規則包)——可以上傳壓縮檔,也可以填一個網址讓系統去下載
攻擊面位置 需要登入且有「建立檢測基準」權限的帳號(客戶的管理員層級)。選「填網址」這條路就能把一個惡意壓縮檔放在自己的網站上給系統抓
信任邊界位置 【連線 C19、C02:網址型規則包由 api 下載(C19),沒過壓縮炸彈上限】
外部網址的內容 ↔︎ 解析程式。防壓縮炸彈的三道上限(檔案數/解開總量/膨脹倍數)應該放在兩種來源都會經過的那一層;當時只接在「上傳檔案」那條路,網址下載回來直接解開,三道一道都不會碰到
元件端點位置 套件 jedi_detection/api/routing.py:POST /detection-tool-profiles(DetectionProfilesRoute.post,網址型走 JSON)、POST /detection-tool-profiles/{uid}/new-version → app/service/detection_profile_extraction_service.py → 兩種來源共用的 common/profile_extractor/inspec.py 的 _read_entries();上限實作新開在 common/archive_limits.py
駭客怎麼打 ① 有建立檢測基準權限的帳號;② 在自己的網站放一個 45MB、解開數十 GB 的壓縮檔;③ 新增檢測基準時選「網址」、填這個網址;④ 系統下載回來直接解開,上傳那條路的三道檢查完全沒跑;⑤ 伺服器記憶體被吃光,所有客戶一起停擺
得手什麼 讓整個產品對所有客戶停擺
修正的做法 ① 三道上限下沉到兩種來源唯一共用的那一層:新開 archive_limits.py,接進 _read_entries()——現有兩種與未來第三種來源都天然涵蓋;② 刻意不在網址分支另補一次檢查,那是補第二條路,下次加來源照樣會漏;③ 原本只住在上傳驗證器裡的那份實作改成共用同一份,兩處不再各有一套;④ 網址下載本身原已邊收邊算、上限 50MB,不必另補
狀態 ✅ 已修(CM-2057,套件 commit c0fe19fd,1.21.0 出貨)。驗證:隨包規則包重打包前後一致;膨脹倍數單測 2MB 宣告 500MB 被擋
§9

M03-4 🟡 規則包「最多一萬個檔」的上限對 zip 形同虛設

欄位 內容
嚴重度 🟡 中(總表 #77;OWASP:A06 不安全的設計)
在哪裡 弱點檢測整合模組:上傳檢測規則包時的「最多一萬個檔」檢查
攻擊面位置 需要登入且有「建立檢測基準」權限的帳號,上傳一個特製的 zip 檔
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
上傳的壓縮檔 ↔︎ 解析程式。檔案數上限應該在打開壓縮檔之前就檢查;當時要等整份檔案清單展開進記憶體之後才開始數,數到第一萬零一個才喊停時記憶體早就吃掉了。程式旁的說明文字還寫反(說「不會先把清單整份展開」,對 tar 成立、對 zip 不成立),這句話正是它通過歷次檢視的原因
元件端點位置 套件 jedi_detection/api/routing.py:POST /detection-tool-profiles(上傳型)→ common/detection_profile_archive.py 的 ProfileArchiveValidator(驗證器)與抽取器兩個入口;開檔前的目錄筆數檢查在 common/archive_limits.py
駭客怎麼打 ① 有建立檢測基準權限的帳號;② 做一個 49.7MB、裡面塞 59.5 萬個空檔的 zip;③ 上傳;④ 系統光是打開它就多吃 360MB 記憶體,之後才報「壓縮檔不安全」——日誌看起來一切正常,代價已經付掉;⑤ 連送幾次,伺服器停擺
得手什麼 讓伺服器停擺,而且日誌看不出異狀
修正的做法 ① 打開之前先讀壓縮檔檔尾的目錄筆數欄位(含大型 zip 的延伸格式),超過上限直接擋,根本不建立檔案清單;② 驗證器與抽取器兩個入口都加;③ 改掉那段寫反的說明文字;④ tar 格式維持逐筆計數(tar 本來就是邊讀邊數)
狀態 ✅ 已修(CM-2057,套件 commit c0fe19fd,1.21.0 出貨)。驗證:手造 6 萬筆 zip 在讀目錄階段即被擋(兩個入口都試過);1.2 萬筆 tar.gz 逐筆計數擋下
§10

M03-10 🟡 手動重新解析無條件開一條背景工作,可以把主機打掛

欄位 內容
嚴重度 🟡 中(總表 #80;OWASP:A06 不安全的設計)
在哪裡 弱點檢測整合模組:檢測基準的「手動重新解析」按鈕(解析失敗後的補救途徑)
攻擊面位置 需要登入且有「建立檢測基準」權限的帳號。按鈕每按一次就是一個請求,可以用程式連打
信任邊界位置 【連線 C02、C09:手動重新解析無上限地入列背景工作】
使用者請求 ↔︎ 背景工作。開背景工作之前應該先問「這一版是不是已經在跑」與「現在總共跑了幾條」;當時每收一次請求就開一條,沒有任何上限。而且這支會先把狀態壓回「等待中」再排工作——如果只在後面加判斷,判斷永遠看到「等待中」、擋不到任何東西
元件端點位置 套件 jedi_detection/api/routing.py:POST /detection-tool-profile-versions/{uid}/extraction(DetectionProfileVersionExtractionRoute.post)→ app/service/detection_profile_extraction_service.py 的 retry() → schedule();並行控管新開在 common/extraction_concurrency.py;上限設定在主專案 config/config.py(DETECTION_PROFILE_EXTRACTION_MAX_CONCURRENT)
駭客怎麼打 ① 有檢測基準權限的帳號;② 對同一版規則包連打幾百次「重新解析」;③ 每一次都開一條背景工作,每條把最大 50MB 的檔整包讀進記憶體、再開一支最長 15 分鐘的外部程式;④ 幾百條同時存在,同一台機器上所有客戶一起變慢
得手什麼 讓同一台機器上的所有客戶一起變慢甚至停擺
修正的做法 ① 同一版去重:同一版已在跑就回 409「這一版正在解析」;② 同時執行上限 4 條:超出的排隊等、不丟棄(丟棄會讓那一版永遠轉圈);③ 排隊深度上限(上限的 10 倍),滿了才回 409「系統忙」;④ 🔴 新判斷放在「壓回等待中」之前——放在之後看起來正確卻一個都擋不到;⑤ 名額在背景工作自己結束時才歸還;自動重抽那條路也走同一道控管
狀態 ✅ 已修(CM-2058,套件 commit f8e3281a、主專案設定 commit ebc57b96f,1.21.0 出貨)。驗證:同版第二次被拒;排隊滿 40 後拒收
§11

M03-11 🟡 換掃描工具時舊的超大網段原封不動跟過去

欄位 內容
嚴重度 🟡 中(總表 #81;OWASP:A06 不安全的設計)
在哪裡 弱點檢測整合模組:任務綁定檢測工具時的「要掃哪些機器」,以及按下執行時把網段逐台展開
攻擊面位置 需要登入且能新增或修改專案任務的帳號(專案管理者層級)
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者送的部分更新 ↔︎ 已存的綁定。台數上限應該用「這次更新完成後實際會生效的值」重新檢查;當時只看「這次有沒有送範圍」,沒送就沿用舊值、不重驗。而展開網段的那一端註明「這裡刻意不擋」,完全相信前面已經檢查過
元件端點位置 主專案 POST /project/{project_uid}/assessment-object/{ao_uid}/jobs(ProjectJobCreateResource)、PUT /project/{project_uid}/job/{job_uid}(ProjectJobDetailResource.put)→ app/flow_control/service/job_service.py 的 create_job()/update_job() → 套件 mixin jedi_detection/app/service/detection_job_binding_handler.py 的 _replace_detection_tool_binding()(工具參數)與 _replace_detection_tool_agent_assignments()(分派列);執行:POST /detection-tools/jobs/{job_uid}/execute → app/service/detection_orchestration_service.py 的 _expand_scan_target_fields()
駭客怎麼打 ① 專案管理者先把任務綁一支不設台數上限的工具,掃描範圍填一個超大網段(例如一個 /8);② 再送第二次修改,只把工具換成有台數上限的那支、不帶範圍;③ 系統只看「這次沒送範圍」就不檢查,舊的超大網段原封不動留在新工具底下;④ 按下執行,系統把網段逐台展開——1,677 萬台,記憶體瞬間吃爆、服務當掉;⑤ 統籌者驗收時另找到同一個洞在分派列那一頭的第二個發生點
得手什麼 一次執行就讓整個服務當掉
修正的做法 ① 工具參數區:新增 _effective_hosts(),這次沒送範圍時拿既有綁定的值,用新工具的上限重驗;② 分派列:原本「沒送分派就直接返回」,改成先拿既有各列的範圍對本次生效的工具重驗一次;③ 展開端加絕對上限 65536 台(新錯誤碼 DETECTION_TOOLS_400020),用「算數量、不展開」的方式判斷——為了擋「展開會炸」而先展開一次等於先炸一次;④ 改掉「這裡刻意不擋」那段說明,否則下一個人會以為新上限是誤加而拿掉
狀態 ✅ 已修(CM-2058,套件 commit f8e3281a,1.21.0 出貨)。驗證:_effective_hosts 四種情境(沒送/送了沒該欄/送新值/明確清空)皆正確;/8 網段算出 16,777,214 台被擋
§12

M04-7 🟡 一次要求回傳幾筆資料,沒有上限

欄位 內容
嚴重度 🟡 中(總表 #83;OWASP:A06 不安全的設計)
在哪裡 共用基礎:全站所有清單畫面共用的「第幾頁、一頁幾筆」設定——11 支套件加主專案都吃這一份
攻擊面位置 任何一個登入帳號,在任何清單畫面的請求裡改一個數字
信任邊界位置 【連線 C02、C05:一頁幾筆沒上限,資料庫整批撈出】
使用者 ↔︎ 資料庫。「一頁幾筆」是使用者填的,應該有上限;當時「第幾頁」有檢查下限,「一頁幾筆」的上限漏掉了一半。既有的「請求大小上限」擋不住,因為送出去的請求很小、要回來的資料很大
元件端點位置 套件 jedi-common/jedi_common/interfaces/schema/common.py:PagerSchema.page_size(RequestMetaSchema 的分頁欄位);所有繼承它的清單端點,例如主專案 POST /projects/list、套件 POST /devices
駭客怎麼打 ① 任何一個登入帳號,打開任一個清單頁;② 把請求裡的「一頁幾筆」改成一個超大數字;③ 資料庫照做,把整張表一次撈出來、組好回傳;④ 重複幾次,整個產品對所有客戶停止回應
得手什麼 讓整個產品對所有客戶停止回應
修正的做法 ① 先盤點再加上限(順序不能顛倒):有些統計是「撈全部回來自己數」,直接加上限會讓數字悄悄變小;grep 全部繼承者與呼叫端,現況最大請求值就是 1000;② page_size 加範圍檢查 1~1000(MAX_PAGE_SIZE),等於現況最大值、不擋既有功能;③ 不開白名單參數繞過——參數能繞等於沒設上限;真的要全撈走專屬匯出或內部分頁迴圈;④ 一處改、全站十二個地方一起生效
狀態 ✅ 已修(CM-2065/FR-114.5C-4,套件 commit a9562dd0,1.21.0 出貨)。驗證:新增測試,突變拿掉上限即轉紅
§13

M06-8 🟡 一筆空白內容的流程範本讓所有人的清單頁一直出錯

欄位 內容
嚴重度 🟡 中(總表 #87;OWASP:A10 例外狀況處理不當)
在哪裡 稽核流程模組:流程範本的儲存與讀取(法規框架裡的流程圖、每輪稽核的流程)
攻擊面位置 需要登入且持有「法規框架修改」權限(module-frame.update)。寫壞一筆是一次性動作,之後受害的是其他正常使用者,不是寫壞它的人
信任邊界位置 【連線 C02、C05:空白內容流程範本存進資料庫,讀取端無防呆】
寫入端 ↔︎ 資料庫 ↔︎ 所有讀取端。寫入端用「這欄有沒有值」當判斷,空字串在程式裡算「沒有值」,就跳過驗證直接存;讀取端又相信資料庫裡的一定是合法流程圖、無條件解析不防呆。兩邊都沒守
元件端點位置 寫入:主專案 api/module_frame/__init__.py 的 PUT /module-frame/item/xml/{uid}(ModuleFrameItemXmlRoute,xml 欄位 payload.get("xml", ""))→ 套件 jedi_flow_engine/infra/repository/workflow_template_repo_impl.py 的 add()/update();讀取:jedi_flow_engine/infra/mapper/workflow_template_mapper.py 的 to_entity()(所有查詢的唯一轉換點),例如 GET /flow-engine/process-definition/{uid} 與各範本清單
駭客怎麼打 ① 一個有法規框架修改權限的帳號,打開流程圖編輯;② 送出時把流程圖內容留空(或直接不帶);③ 寫入端判斷「沒有值」就跳過檢查,空字串照樣存進資料庫;④ 之後任何人打開會讀到這筆範本的清單或詳細頁,解析器對空字串報錯、整頁 500;⑤ 不會自己恢復,要有人進資料庫把那筆修掉
得手什麼 讓所有人的流程範本清單與相關頁面持續打不開
修正的做法 ① 寫入端分清三種情況:抽出 _sync_json_from_xml()——沒送(維持舊值)、空白字串(直接拒絕,新錯誤碼 FLOW_ENGINE_400004)、有值(解析並驗證);② 讀取端防呆:to_entity() 遇空值跳過解析、解析失敗退成「0 個任務」並記警告,一筆壞資料不再拖垮整份查詢;③ 主專案這邊範本的新增與修改也補上「先驗流程圖再存」,原本只有發布時才驗
狀態 ✅ 已修(CM-2059,套件 commit 4a416ece、主專案 commit 8edbaf944,1.21.0 出貨)。驗證:DEV 27 份範本修前修後逐份比對走訪結果一致,0 份被新規則擋下
§14

M14-1 🟡 聊天沒有長度上限,也沒有次數限制

欄位 內容
嚴重度 🟡 中(總表 #94;OWASP:A06 不安全的設計)
在哪裡 AI 聊天助手模組:產品裡的聊天框,使用者打的內容直接轉給外部 AI
攻擊面位置 任何一個最基層的員工,不需要特殊權限,四到八個連線就夠
信任邊界位置 【連線 C13、C02:聊天內容經 api 轉給外部 AI,沒限長度與次數、逾時 10 分鐘】
使用者 ↔︎ 外部 AI 服務。轉給外部服務之前應該限制訊息長度、每人呼叫次數,並設等待逾時;當時三樣都沒有——外部 AI 沒回應時要等 10 分鐘(套件預設值),而系統 120 秒就會強制砍掉卡住的請求,這中間的落差正是處理程序被佔住的原因。整站「請求不得超過 50MB」的限制對聊天訊息等於沒擋
元件端點位置 套件 jedi-ai-bot:POST /api/1.0/ai-chatbot(AiBotRoute.post,路徑在 plugin/contract.py 的 DEFAULT_ENDPOINT,plugin/assembly.py 掛載)→ app/service/ai_bot_service.py;主專案接線 core/plugins/ai_bot.py、計次器 common/util/redis_rate_limiter.py、429 處理在 core/app_factory.py
駭客怎麼打 ① 任何一個登入的員工,打開聊天框;② 開四到八個分頁,各送一則超大的訊息;③ 每一則都直接轉給外部 AI、等它回,外部回得慢就一直等;④ 4 條處理程序全被佔住,其他人連登入畫面都打不開;⑤ 另一種後果是把公司共用的 AI 額度燒光,直接變成帳單
得手什麼 讓整個產品對所有人停止回應,或燒光公司的 AI 額度
修正的做法 ① 訊息長度上限 4000 字,在路由最外層先擋,超過回 400;② 呼叫外部 AI 設 60 秒逾時,讓系統自己放棄、不等到被強制砍掉;③ 套件開「每人每分鐘最多幾次」的插槽,主專案用 Redis 計次器接上,預設每人每分鐘 10 次,超過回 429 並帶「幾秒後可再試」;計次跨處理程序共用;④ AI 儀表板同款問題同一次修(每分鐘 3 次,因為一次生成打兩次 AI);⑤ 限流刻意故障時放行——它是防濫用不是身分疆界,擋掉所有正常客戶的代價更高
狀態 ✅ 已修(CM-2064,套件 commit 248132ec、主專案 commit 0d321d98a,1.21.0 出貨)。驗證:正常 200、5000 字 400、第 11 次 429 帶等待秒數、計次器故障時放行
§15

總表#103 🟡 寫日誌進資料庫失敗會連帶弄壞使用者的請求

欄位 內容
嚴重度 🟡 中(總表 #103;OWASP:A10 例外狀況處理不當、A09 日誌與告警失效)
在哪裡 共用基礎:系統把操作紀錄與錯誤寫進資料庫的那一段(每個請求都可能經過)
攻擊面位置 不是駭客能主動挑的入口(檢查員三票一致認定非資安問題);只要資料庫那一刻寫不進去(連線斷、表被鎖、權限被收回),任何正常使用者的請求都可能被連累。正式環境原本不走這條路,CM-1920 為了「只放行稽核事件與錯誤」在正式/STG 環境補掛了這段,風險因此放大
信任邊界位置 【連線 C05:api → 資料庫(資料庫隔離規則在這條線另一端把關);寫日誌進資料庫失敗連帶弄壞請求】
業務程式 ↔︎ 紀錄機制。紀錄是附帶動作,失敗時應該自己吞掉、印到旁邊;當時寫資料庫那支完全沒有錯誤處理,錯誤沿著「記一筆日誌」的呼叫點往上冒,回到業務程式變成使用者看到的系統錯誤
元件端點位置 套件 jedi-common/jedi_common/logger/db_log/db_handler.py:DBLogHandler.emit();掛載設定在同套件 logger/config_prod.py(CM-1920 commit 4bf7906 補掛)
駭客怎麼打 ①(不是駭客主動打)一般使用者在正式環境正常操作,某個動作會記一筆紀錄;② 那一刻資料庫寫不進去;③ 錯誤從紀錄機制一路往上冒,把使用者原本已經做完的請求一起弄成失敗;④ 另一個隱藏問題:這支用到的時間欄位要靠前面的紀錄處理器順便填好,它能正常運作純粹是因為剛好排在第三個,換個順序就會自己出錯
得手什麼 沒有人得手;「記錄失敗」這種小事變成使用者的操作失敗
修正的做法 ① emit() 整支包上錯誤處理,失敗時呼叫紀錄機制的標準出口 handleError(record)(印到錯誤輸出、不回呼紀錄機制,不會無限迴圈);② 拆掉隱性的排序依賴:時間改讀一定有值的 record.created,不再依賴前一個處理器填好的格式化時間;③ 操作者帳號與暱稱改成「有就記、沒有就空著」,缺了只少記操作者、不再出錯
狀態 ✅ 已修(CM-2065/FR-114.5C-4,套件 commit a9562dd0,1.21.0 出貨)
§16

M20-4 🟡 兩支半夜清理程式靠「找不到身分就給最高權限」才跑得動

欄位 內容
嚴重度 🟡 中(總表 #108;OWASP:A10 例外狀況處理不當、A01 存取控制失效)
在哪裡 客戶資料隔離專項:每天凌晨兩支自動清理程式——01:00 清法規框架解析的舊工作單、03:10 清「指向已刪除任務」的孤兒資料
攻擊面位置 不是攻擊面,是規劃上的相依:兩支程式沒有使用者身分,當時能看到全部客戶資料,靠的是資料庫連線那層「沒有身分就給最高權限」的放行規則(CM-1559 已記錄那條規則本身就是個洞)
信任邊界位置 【連線 C09、C05:排程 worker 靠「找不到身分就給最高權限」讀資料庫】
背景程式 ↔︎ 資料庫隔離規則。要跨客戶掃描的背景程式應該顯式宣告「我是系統、我要跨客戶」,而不是靠「找不到身分」這個失敗狀態被放行;靠失敗狀態放行的那一天被收緊,兩支程式會看不到任何資料、安靜地什麼都不做
元件端點位置 主專案 core/scheduler.py:_framework_parse_job_cleanup_tick()(01:00)、_job_binding_orphan_cleanup_tick()(03:10)→ app/flow_control/service/job_binding_orphan_cleanup_service.py 的 run_once();具名身分工具在套件 jedi_common.session.auth.system_context
駭客怎麼打 ①(不是駭客,是未來的修補動作)有人去收緊「沒有身分就給最高權限」那條規則(遲早要做,本身就是一個洞);② 兩支清理程式從此連線時看不到任何客戶資料;③ 它們不報錯,只是每晚掃到 0 筆;④ 孤兒資料與過期工作單一直堆積,沒有人發現
得手什麼 沒有人得手;是清理機制在不知不覺中停擺
修正的做法 ① 套件新增 system_context("<排程名>"),把「繞過隔離」從失敗狀態的副作用改成一個要顯式宣告、有名字的動作;② 兩支排程各自包上具名系統身分;孤兒清理那支要在服務自開資料庫連線之前就設好身分,否則那條連線讀不到;③ 原本「沒有身分就給最高權限」的說明文字改寫成反向警語;④ 加守衛測試掃排程程式碼,確認跨客戶的排程都有具名身分,突變(拿掉)會轉紅
狀態 ✅ 已修(CM-1767/FR-094.1a,主專案 commit 3af065133、套件 commit 2bd93918)。2026-09-16 複查確認兩支都以具名系統身分執行
§17

M11-23 🟡 系統安全計畫的 Excel 匯入只量上傳檔、不量解開後多大

欄位 內容
嚴重度 🟡 中(總表 #150;OWASP:A06 不安全的設計)
在哪裡 合規文件核心:合規資源庫與專案的系統安全計畫(SSP)Excel 匯入
攻擊面位置 任何能匯入系統安全計畫的登入帳號,上傳一份特製的 Excel
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者上傳的檔案 ↔︎ 解析程式。Excel 其實是一個壓縮檔,檢查應該在打開之前就問「解開後多大」;當時只量上傳檔本身多大,打開時整份攤進記憶體,而且讀取方式是「每取一格都從頭掃」
元件端點位置 主專案 api/oscal/__init__.py:POST /ssp-excel-imports/parse(SspExcelImportParseRoute)、POST /ssp/{ssp_uid}/excel-import/upload(SspScopedExcelImportUploadRoute)→ app/oscal/service/ssp_excel_import_app_service.py → app/oscal/service/excel_parser/parser.py、sheet_handlers.py;共用檢查在套件 jedi_compliance_audit/app/service/import_adapter/archive_guard.py 的 check_ooxml_archive()
駭客怎麼打 ① 任何能匯入系統安全計畫的帳號;② 做一份 2MB、解開好幾 GB 的「試算表炸彈」;③ 上傳匯入;④ 系統打開時整份塞進記憶體,處理程序記憶體爆掉被砍,同一台上其他人的請求一起失敗;⑤ 每分鐘送幾次,產品就持續不可用
得手什麼 讓同一台主機上所有人的請求持續失敗
修正的做法 ① 開檔前只讀壓縮檔目錄、不解壓:解開後總量上限 200MB、最多 5,000 段、單段 1MB 以上者膨脹比不得超過 100 倍,不是合法壓縮檔也拒;超限轉成既有的「解析失敗」狀態,沒新增錯誤碼;② 四個入口共用同一支檢查(SSP Excel、SSP Word、稽核計畫 Word、稽核結果 Excel),放在套件裡;③ Excel 改成唯讀串流、一列一列讀,讀完關檔;④ 上限依真實檔實測訂,至少 15 倍餘裕
狀態 ✅ 已修(FR-114 CM-2181,主專案 commit 48638cbdb、套件 commit 2cb9d2cd,1.21.0 出貨;同型第五入口見 M06-23)。驗證:解開 400MB 的 Excel 炸彈 0.37 秒回失敗、處理程序記憶體不變;新舊程式對真實範本解析結果逐位元組相同
§18

M11-24 🟡 系統安全計畫的 Word 匯入同樣只量上傳檔大小

欄位 內容
嚴重度 🟡 中(總表 #152;OWASP:A06 不安全的設計)
在哪裡 合規文件核心:專案的系統安全計畫 Word 匯入
攻擊面位置 任何能匯入系統安全計畫的登入帳號,上傳一份特製的 Word
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者上傳的檔案 ↔︎ 解析程式。Word 也是壓縮檔,同 M11-23;而且同一份上傳會被完整打開三次,所以檢查必須放在第一次打開之前
元件端點位置 主專案 api/oscal/__init__.py:POST /ssp-docx-imports/parse(SspDocxImportParseRoute)→ app/oscal/service/ssp_docx_import_app_service.py(第一次開檔在 _normalize_docx_revisions 之前);共用檢查 jedi_compliance_audit/app/service/import_adapter/archive_guard.py 的 check_ooxml_archive()
駭客怎麼打 ① 任何能匯入系統安全計畫的帳號;② 做一份 0.32MB、裡面 300 段各 1MB 的 Word;③ 上傳;④ 打開一次就吃掉 1.9GB 記憶體,而且會被打開三次;⑤ 處理程序被砍,同一台上其他人一起失敗
得手什麼 用不到 1MB 的檔讓伺服器記憶體耗盡
修正的做法 ① 在第一次開檔(整理修訂紀錄那一步)之前呼叫同一支 check_ooxml_archive(),之後的兩次開檔自然受保護;② 超限丟例外,被既有錯誤處理轉成「解析失敗」狀態與既有錯誤碼,沒新增錯誤碼;③ 與 M11-23、M12-2、M12-3 同一張卡、同一支檢查函式
狀態 ✅ 已修(FR-114 CM-2181,主專案 commit 48638cbdb、套件 commit 2cb9d2cd,1.21.0 出貨)。驗證:解開 300MB 的 Word 炸彈 0.33 秒回失敗、處理程序記憶體 449→449MB 不漲;正常 SSP Word 照常進入待審
§19

M11-25 🟡 Excel「說明」頁填一百萬個數字,版本號比對就卡住

欄位 內容
嚴重度 🟡 中(總表 #154;OWASP:A06 不安全的設計)
在哪裡 合規文件核心:系統安全計畫 Excel 匯入時,檢查「說明」頁那一格的範本版本號
攻擊面位置 任何能匯入系統安全計畫的登入帳號,在 Excel 的說明頁版本格填一長串數字
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
檔案內容 ↔︎ 比對規則。版本號的比對規則應該頭尾綁定、位數有上限;當時系統裡寫了兩份版本規則,一份寫對、一份寫錯(沒綁頭尾、沒位數上限),解析時用的是寫錯那份,遇到超長數字會反覆重試
元件端點位置 主專案 POST /ssp-excel-imports/parse、POST /ssp/{ssp_uid}/excel-import/upload → app/oscal/service/excel_parser/parser.py(原 _SEMVER_PATTERN)→ 改用 app/oscal/service/excel_parser/version_check.py 新增的 extract_version()
駭客怎麼打 ① 任何能匯入系統安全計畫的帳號;② 拿一份正常範本,在說明頁版本那一格填一百萬個數字;③ 上傳;④ 版本號比對卡住,一個請求佔住處理程序到 120 秒逾時;⑤ 四個請求讓整個系統沒回應
得手什麼 用四個請求讓整個系統沒回應
修正的做法 ① 刪掉寫錯的那份規則,版本規則只留 version_check.py 一處;② 新增 extract_version():輸入先截 200 字,規則限制每段最多 5 位數
狀態 ✅ 已修(FR-114 CM-2181,主專案 commit 48638cbdb,1.21.0 出貨)。驗證:版本格塞一百萬位數字 0.24 秒回格式錯誤(舊規則 5 萬位就要 5 秒)
§20

M11-26 🟡 一份幾 KB 的特製 Word 讓系統安全計畫匯入卡到逾時

欄位 內容
嚴重度 🟡 中(總表 #175;OWASP:A06 不安全的設計)
在哪裡 合規文件核心:系統安全計畫 Word 匯入的整條解析路(找佔位字、比對控制項編號、走訪段落與表格、取冒號後的文字)
攻擊面位置 任何能匯入系統安全計畫的帳號;壓縮炸彈(M11-24)修好之後門檻升一級,但幾 KB 的檔就夠
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
檔案內容 ↔︎ 解析程式。解析在請求當下同步做完、佔住一條處理程序;解析路上六個地方遇到刻意做壞的內容會退化成平方或三次方時間——比對規則會回頭重試、每取一段都重建整份段落清單、每取一次樣式都線性掃樣式表、表格一格宣告一億欄就展開成一億個物件
元件端點位置 主專案 POST /ssp-docx-imports/parse → app/oscal/service/ssp_docx_import_app_service.py → domain/oscal/parser/docx_parser_core.py、docx_section_extractors.py、control_id_matcher.py、domain/oscal/adapter/cmmc_ssp_adapter.py;新增結構檢查 domain/oscal/parser/docx_input_limits.py 的 check_docx_structure()
駭客怎麼打 ① 能匯入系統安全計畫的帳號;② 拿一份真實的 SSP Word,在佔位字那一段塞 3,200 個空白,或塞兩三千個空段落,或讓一格表格宣告一億欄;③ 上傳;④ 解析卡 46~96 秒,表格那種直接逾時被砍;⑤ 四個請求同時送,整個產品對所有客戶停擺
得手什麼 用幾 KB 的檔讓整個產品停擺
修正的做法 ① 解析前先掃一次結構:段落(含表格格內)超過 3,000、任一格合併超過 200 欄、表格超過 200 欄就拒收——真實 SSP 約 600 段,上限約 5 倍;整份先擋一次,12 個展開表格的呼叫點不必各自改;② 樣式名稱依文件快取,不再每段線性掃樣式表(佔總耗時 95%);③ 佔位字四條規則改成「吃了不吐」的寫法,判準不變;超過 500 字直接判「不是佔位字」;④ 控制項編號比對排除同種開括號並限長 200;⑤ 段落走訪改成直接包底層元素,不再每次重建清單;兩處「找自己在第幾段」改成外層計數;⑥ 取冒號後文字輸入先截 2,000 字
狀態 ✅ 已修(FR-114 CM-2182,主專案 commit 9edcf4de4,1.21.0 出貨)。驗證:五種惡意形狀全帶 81 秒→0.49 秒、加上一億欄 逾時→0.04 秒拒收;7 份真實 SSP 新舊輸出逐位元組相同;惡意檔解析期間正常請求 0.09 秒回應
§21

M12-2 🟡 稽核計畫 Word 匯入的壓縮炸彈

欄位 內容
嚴重度 🟡 中(總表 #176;OWASP:A06 不安全的設計)
在哪裡 稽核輪次管理:稽核計畫頁匯入稽核計畫 Word
攻擊面位置 需要是某個專案的稽核員或管理者,且計畫還在規劃階段
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者上傳的檔案 ↔︎ 解析程式。同 M11-23、M11-24 的病,病灶在另一個套件的共用讀檔程式:只量上傳檔大小、不量解開後多大,打開時整份塞進記憶體
元件端點位置 主專案 api/project/__init__.py:POST /ap/{ap_uid}/docx-imports/parse(ApDocxImportUploadRoute)→ 套件 jedi_compliance_audit/app/service/ap_docx_import_app_service.py 的 upload_and_parse() → app/service/import_adapter/registry_base.py 的 RegistryBase.parse_file()
駭客怎麼打 ① 某專案的稽核員或管理者;② 做一份不到 20MB、解開幾十 GB 的 Word;③ 在稽核計畫頁匯入;④ 處理程序被砍;⑤ 連送幾份,所有客戶一起停擺
得手什麼 讓所有客戶一起停擺
修正的做法 ① 在這個模組共用的讀檔程式 RegistryBase.parse_file() 裡,開檔之前呼叫同一支 check_ooxml_archive(),稽核計畫 Word 與稽核結果 Excel 一次蓋到;② 不論成敗都在最後關閉活頁簿(唯讀模式會一直握著檔案);③ 與 M11-23、M11-24、M12-3 四個入口同一張卡、同一支檢查函式
狀態 ✅ 已修(FR-114 CM-2181,套件 commit 2cb9d2cd、主專案 commit 48638cbdb,1.21.0 出貨)。驗證:Word 炸彈(解開 300MB)修前吃到 681MB 後炸、修後 0 秒拒絕;DEV 經 API 打 0.3~0.6 秒回失敗、處理程序記憶體不變
§22

M12-3 🟡 稽核紀錄 Excel 在很後面填一格,系統就從頭一列列讀到那裡

欄位 內容
嚴重度 🟡 中(總表 #177;OWASP:A06 不安全的設計)
在哪裡 稽核輪次管理:匯入稽核結果(稽核紀錄 Excel)
攻擊面位置 需要是某個專案的稽核員或管理者。成本比壓縮炸彈更低——幾 KB、看起來正常的稽核表就能騙過格式檢查
信任邊界位置 【連線 C02、C05:讀 Excel 期間資料庫交易一直開著】
檔案內容 ↔︎ 解析程式。Excel 的「有資料的範圍」是檔案自己宣告的,解析程式應該設列數上限、遇到大段空白就停;當時從第一列一列列讀到最後一格有值的列,每一列都建物件,而且這段時間資料庫交易一直開著
元件端點位置 主專案 api/project/__init__.py:POST /audit-round/{round_uid}/ar-imports/parse(ArImportUploadRoute)→ 套件 jedi_compliance_audit/app/service/ar_import_app_service.py → app/service/ar_report_parser/airasia_cmmc_l1_ar_v1.py 的 _parse_rows()
駭客怎麼打 ① 某專案的稽核員或管理者;② 拿一份正常的稽核表,在第 20 萬列填一格;③ 匯入,檔案只有 4.9KB;④ 系統從頭讀到第 20 萬列,耗時 11 秒、吃掉 355MB;推算填到 Excel 最後一列約 57 秒、1.9GB;⑤ 連送幾份,處理程序全被佔住
得手什麼 用幾 KB 的普通檔佔住處理程序與資料庫交易
修正的做法 ① 稽核結果改用唯讀模式、一列一列串流讀;② 連續 200 列全空就視為檔尾;③ 超過 5,000 列資料直接報錯(真實表只有一百多列);④ 表頭與基本資料那幾格(只有前 5 列)照舊直接取
狀態 ✅ 已修(FR-114 CM-2181,套件 commit 2cb9d2cd、主專案 commit 48638cbdb,1.21.0 出貨)。驗證:第 20 萬列填一格修前 1.6 秒/505MB、修後 0.01 秒/51MB;真實稽核表新舊解析結果逐位元組相同
§23

M06-23 🟡 匯入任務的 Excel 沒有上傳檢查、整份攤開在記憶體

欄位 內容
嚴重度 🟡 中(總表 #207;OWASP:A06 不安全的設計)
在哪裡 稽核流程:專案規劃頁的「匯入任務 Excel」——「試算表炸彈」的第五個入口,先前修好的四個入口沒列到它
攻擊面位置 需要登入且有該專案任務匯入權限的帳號
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者上傳的檔案 ↔︎ 解析程式。同 M11-23;這個入口用 load_workbook(file) 一次把整份攤開,沒有任何壓縮炸彈檢查
元件端點位置 套件 jedi_task_platform/task/api/routing.py:POST /project/{project_uid}/ap/{ap_uid}/jobs/import(JobImportRoute.post)→ 主專案 app/flow_control/service/job_import_service.py 的 validate_import() → 新增的 _iter_upload_rows()、_iter_data_rows()
駭客怎麼打 ① 有任務匯入權限的帳號;② 做一份 9.8MB 的特製 Excel;③ 在規劃頁匯入任務;④ 系統讀 22 秒、吃掉 1.46GB;⑤ 連送幾份,所有客戶一起連不上。另一種:4.8KB 的檔只在第 1,048,000 列放一格,要迭代一百萬列
得手什麼 讓所有客戶一起連不上
修正的做法 ① 開檔前用 CM-2181 的共用檢查 check_ooxml_archive() 擋炸彈,不另寫第二套;② 改唯讀模式逐列讀;並重設檔案自己宣告的範圍,避免宣告寫小的檔少讀資料列;③ 首腦驗收退回後補:連續 200 列空白即停、資料超過 5,000 列報錯,與稽核結果 Excel 同規則;④ 任何失敗(含迭代途中才發現的毀損)都轉既有的「檔案格式錯誤」,不新增錯誤碼
狀態 ✅ 已修(CM-2203,主專案 commit eb4ff79cd+040c3c827,1.21.0 出貨)。驗證:壓縮炸彈 0.22 秒回 400、記憶體不漲;第 1,048,000 列放一格的檔 0.34 秒整趟回應;DEV 兩份真實匯出檔新舊回應逐字相同;突變拿掉檢查即放行
§24

M02-12 🟡 刪原廠共用檔時實體先被刪、紀錄卻刪不掉

欄位 內容
嚴重度 🟡 中(總表 #212;OWASP:A01 存取控制失效、A10 例外狀況處理不當)
在哪裡 檔案上傳下載模組:刪除檔案;受害的是原廠給所有客戶共用的檔案(框架匯入的公版指引、檢測規則包等)
攻擊面位置 任何一家客戶的一般登入帳號都構成攻擊面,只要知道一個原廠共用檔的編號
信任邊界位置 【連線 C02、C07:先刪檔案儲存的實體、才刪資料庫紀錄】
客戶 ↔︎ 原廠共用資源。刪除時應該先問「這個檔是不是原廠的、你能不能刪」,而且資料庫紀錄沒刪成就不能動實體檔。當時順序是先刪實體、再刪紀錄;紀錄那一步被資料庫隔離規則擋下,但實體已經沒了,而程式不看刪除結果、照樣回成功
元件端點位置 jedi_file_upload/api/routing.py:DELETE /file/upload/{uid}(UploadFileRoute.delete)→ 儲存後端 infra/adapter/minio/minio_adapter.py(落地版的 SeaweedFS 也走這支)與 infra/adapter/local/local_file_adapter.py 的 delete_file()/delete_files_by_uids();歸屬檢查在主專案 core/plugins/file_upload.py 的 SystemAssetFileOwnershipProvider
駭客怎麼打 ① 任何一家客戶的一般員工,在畫面上看到一個原廠範本檔的編號;② 對這個編號按刪除;③ 系統先把儲存空間裡的實體檔刪掉;④ 接著刪資料庫紀錄時被隔離規則擋下(不是原廠),但程式沒看結果,回「刪除成功」;⑤ 紀錄還在、檔案沒了,所有客戶打開這個範本都是壞檔
得手什麼 一個帳號就讓原廠給所有客戶的共用範本全部打不開
修正的做法 分兩步:① 原廠共用檔禁刪:歸屬登記表裡,原廠共用檔對「寫入(刪除)」一律回「找不到」,只有平台管理員走例外口(CM-2033 接上歸屬檢查時一起做);② 刪除順序反過來:新開共用的 record_first_delete.py,先刪紀錄,紀錄真的刪掉才刪實體;實體刪失敗只記日誌(留孤兒實體檔比留壞紀錄好);批次刪除刪完再查一次,只對真的刪掉的那幾筆動實體;衍生的 PDF 轉檔同樣改順序
狀態 ✅ 已修(禁刪:CM-2033 主專案 commit a334f5385;刪除順序:CM-2228 套件 commit 5ec9b617,1.21.0 出貨)。驗證:新增 10 條測試,突變改回「先刪實體」3 條轉紅
§25

M04-9 ⚪ 兩種運作紀錄的詳細程度被寫死成最詳細

欄位 內容
嚴重度 ⚪ 低(總表 #125;OWASP:A02 安全設定錯誤)
在哪裡 共用基礎:正式環境的紀錄設定——資料庫對映與資料庫事件兩種運作紀錄
攻擊面位置 不是攻擊面:沒有安全問題(這兩種紀錄只寫檔案、不進資料庫),是設定本身讓紀錄量暴增、吃掉磁碟。任何正常運作都會觸發
信任邊界位置 【—(非連線:程式邏輯)】
正式設定 ↔︎ 開發設定。正式環境的紀錄等級應該只留需要的;當時兩處被寫死成最詳細(除錯等級),連輸出到畫面的那一層也寫死最詳細,不跟隨紀錄等級設定
元件端點位置 不是端點,是設定檔:套件 jedi-common/jedi_common/logger/config_prod.py(sqlalchemy.orm、pymongo.event_loggers 兩個紀錄器與 handlers.app.level);選哪份設定由 logger/config_logger.py 依 RUN_ENV 決定
駭客怎麼打 ①(不是駭客)產品在正式環境正常開機運作;② 資料庫對映紀錄每一次設定都寫一行;③ 開機一小時倒出約 7,200 行,其中約 6,050 行是這一種;④ 日積月累吃滿磁碟,主機上其他服務跟著出問題,真正要看的紀錄也被淹沒
得手什麼 沒有人得手;是磁碟被紀錄塞滿
修正的做法 ① 兩處紀錄器從除錯等級調回一般等級(CM-2065);② 實測發現資料庫對映的設定紀錄本身就是一般等級、調回一般擋不住,再改成只留警告以上(CM-2270);③ 輸出那一層不再寫死最詳細,改成「跟隨 LOG_LEVEL、但最多到一般等級」——寫死一般會堵住客戶開除錯排查的路,直接跟隨又會在設成警告時把業務紀錄也砍掉;④ 資料庫事件紀錄器沒有掛任何監聽,一般等級下零輸出,維持不動
狀態 ✅ 已修(CM-2065 套件 commit a9562dd0;補修 CM-2270 套件 commit bf97b904)。驗證:開機約 10 秒,改前輸出 6,096 行(對映紀錄 6,049)、改後 51 行(對映紀錄 0);設 LOG_LEVEL=DEBUG 時除錯紀錄照常出現
§26

M07-8 ⚪ 分類程式沒有記憶體與處理器上限,逾時也殺不掉

欄位 內容
嚴重度 ⚪ 低(總表 #131;OWASP:A06 不安全的設計)
在哪裡 證據自動分類模組:系統開一個獨立的分類程式(容器)去拆解證據檔、交給 AI 判斷
攻擊面位置 需要登入且能對證據批次按「開始分類」的帳號(專案管理者),上傳一份做過手腳的壓縮檔。評低的前提:掃描當時落地版刻意沒裝分類程式的執行環境,客戶端根本啟動不了,只影響我們自己的開發機——這是產品決策不是技術事實,落地版一開放就必須先修
信任邊界位置 【連線 C09、C08:worker 開分類/拆解程式處理證據檔,沒有資源上限】
證據檔內容 ↔︎ 主機資源。拆解不可信檔案的程式應該被關在有上限的籠子裡、逾時就能確實終止;當時沒有記憶體、處理器、程序數上限,逾時時只砍得掉本機的指令,容器本身照跑、照燒 AI 額度
元件端點位置 套件 jedi_evidence_classification/api/routing.py:POST /evidence-batches/{batch_uid}/classify(EvidenceBatchClassifyRoute.post)→ 開發機路徑 infra/classifier_container_runner.py 的 run()(直接起容器);落地版路徑走常駐服務,上限在主專案 docker/production/docker-compose.yml 的 guidant-classifier
駭客怎麼打 ① 專案管理者把一份刻意做過手腳的壓縮檔放進證據批次;② 按開始分類;③ 分類程式解開的當下把主機記憶體吃光,連後端服務一起被系統砍掉;④ 逾時後容器沒被停掉,繼續跑、繼續消耗 AI 額度
得手什麼 讓主機上的後端服務被系統砍掉,並持續燒 AI 額度
修正的做法 ① 落地版改走常駐分類服務時,由部署設定直接限住:記憶體 2G、處理器 2 顆、程序數 256、唯讀檔案系統(CM-2223);② 開發機直接起容器那條路補同樣的上限,並給容器取名 classifier-<工作編號>;③ 逾時時對這個名字下「強制停止」,停不掉只記日誌、不蓋掉原本的逾時錯誤;④ 上限不必精算——只要不是無限大、留得夠給後端服務
狀態 ✅ 已修(CM-2227,套件 commit ecddb72c+85370a17;落地版常駐服務 CM-2223 主專案 commit 750b635de,1.21.0 出貨)。驗證:真實容器檢查到記憶體 2G、處理器 2、程序數 256;4 秒逾時後容器確實已不在
§27

M19-3 ⚪ 設備清單一頁要顯示幾筆,沒有上限

欄位 內容
嚴重度 ⚪ 低(總表 #136;OWASP:A06 不安全的設計)
在哪裡 設備與資訊系統清冊:設備清單頁的「一頁顯示幾筆」
攻擊面位置 任何一個登入帳號,在設備清單的請求裡改一個數字
信任邊界位置 【連線 C02、C05:】
使用者 ↔︎ 資料庫。根源不在這一塊,在所有模組共用的分頁設定(同 M04-7);設備清單的請求格式繼承那一份,所以一起沒有上限
元件端點位置 套件 jedi_asset/api/routing.py:POST /devices(DeviceListRoute,請求格式 DevicePageQueryRequest)→ 繼承 jedi-common/jedi_common/interfaces/schema/common.py 的 PagerSchema
駭客怎麼打 ① 任何一個登入帳號,打開設備清單;② 把「一頁幾筆」改成超大數字;③ 資料庫把整張設備表一次撈出來
得手什麼 消耗資料庫與伺服器資源,重複打可拖慢整個產品
修正的做法 修在共用底層、一處全站生效:PagerSchema.page_size 加上限 1000(取值依據與盤點見 M04-7);設備清單的請求格式繼承它,不必另改
狀態 ✅ 已修(CM-2065,套件 commit a9562dd0,1.21.0 出貨)
§28

總表#145 ⚪ 公告查無此筆對空值設值、沒有登入身分時建立公告崩潰

欄位 內容
嚴重度 ⚪ 低(總表 #145;OWASP:A10 例外狀況處理不當)
在哪裡 公告模組:修改公告、建立公告
攻擊面位置 掃描當時沒有任何呼叫者:主專案走自己那份公告程式,套件那份是死碼,所以打不到。兩處都只會讓該次請求 500,不是資料外洩也不是權限繞過
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
路由層 ↔︎ 服務層的身分傳遞。服務層應該信任路由層交給它的「操作者是誰」,自己不去抓;當時服務層自己去取登入者再取 .uid,取不到時對空值取值直接崩潰。另一處是存在性檢查查錯對象
元件端點位置 套件 jedi_bulletin/api/routing.py:POST /bulletin(BulletinCreateRoute)、PUT /bulletin/{uid}(BulletinDetailRoute.put)→ jedi_bulletin/app/service/bulletin_service.py 的 add_bulletin()/update_bulletin();存在性檢查在 jedi_bulletin/infra/repository/bulletin_repo_impl.py 的 update()
駭客怎麼打 ①(當時沒有入口,以下是有入口時的樣子)有人修改一則不存在的公告;② 程式想確認「這筆存在嗎」,卻檢查了傳進來的參數、不是查詢結果,以為存在;③ 下一行對空值設值,該次請求 500;④ 另一條:背景程式或排程在沒有登入身分的情況下建公告,取登入者編號時崩潰
得手什麼 沒有人得手;是該次請求失敗
修正的做法 公告整塊從主專案搬進套件時一併處理:① 存在性檢查改成檢查查詢結果(if not bulletin_model),查無此筆回空、上層轉成「公告不存在」;② 服務層不再自己抓登入者,改由路由層用 current_user_login_name() 取帳號傳進來,服務層只收「操作者是誰」這個參數
狀態 ✅ 已修(FR-080 第 5 棒/CM-1625,套件 commit 3f27793f)。⚠️ 該 commit 是整塊搬遷,訊息沒有點名本條,修正依據是逐行比對 diff 確認兩處都已改
§29

M11-34 ⚪ 幾 KB 的特製 YAML 範本讓伺服器展開到當掉

欄位 內容
嚴重度 ⚪ 低(總表 #180;OWASP:A06 不安全的設計)
在哪裡 合規文件核心:合規資源庫的「匯入 YAML 範本」
攻擊面位置 需要有建立範本權限(module-frame.create)的管理員帳號;後果是暫停服務、不是外洩
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者上傳的 YAML ↔︎ 解析程式。YAML 支援「引用前面某一段」,解析器在讀檔階段就會把引用全部展開;當時用的安全讀法擋得住執行程式、擋不住引用炸彈(30 層、每層引用 10 次),之後的遞迴展開也沒有層數上限。檔案頂層不是預期格式時還會直接 500
元件端點位置 主專案 api/module_frame/__init__.py:POST /module-frame/import/yaml(ModuleFrameYamlImportTemplateRoute)→ module_frame_import_service.import_yaml_file() → infra/module_frame/adapter/yaml_to_module_frame_parser_adapter.py(_NoAliasSafeLoader、_parse_workflow())
駭客怎麼打 ① 有建立範本權限的管理員;② 寫一個幾 KB 的 YAML,裡面一層引用前一層十次、疊 30 層;③ 上傳匯入;④ 伺服器展開時 CPU 與記憶體吃滿,一個請求佔住處理程序到逾時;⑤ 連送幾個,所有公司的使用者同時連不上
得手什麼 讓所有公司的使用者同時連不上
修正的做法 ① 自訂讀檔器,遇到「引用」直接拒絕——這是主修法,因為解析器在讀檔階段就展開,事後數層數擋不住;repo 內沒有任何範本用到引用,全拒不影響既有範本;② 遞迴展開加層數上限 10、流程與任務節點總數上限 5,000;③ 頂層不是物件、欄位型別不對一律回 400 格式錯誤,不再 500;④ 新錯誤碼撞號後改為 MODULE_FRAME_400011(格式)/MODULE_FRAME_400012(含引用或超限)
狀態 ✅ 已修(FR-114 CM-2190,主專案 commit 374392065;錯誤碼改號 3d97fcaf9,1.21.0 出貨)。驗證:30 層引用炸彈 0 秒回 400;10 層通過、12 層擋下;6,000 節點擋下
§30

M11-35 ⚪ 範本匯入檢核送一格很長的怪字串,佔住處理程序兩分鐘

欄位 內容
嚴重度 ⚪ 低(總表 #181;OWASP:A06 不安全的設計)
在哪裡 合規文件核心:合規資源庫 Excel 匯入前的「檢核」——檢查條款與要求事項欄位有沒有夾帶網頁標籤
攻擊面位置 需要有建立範本權限(module-frame.create)的管理員帳號
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者送的欄位內容 ↔︎ 比對規則。檢查網頁標籤的規則應該限制長度;當時規則可以在一格超長、充滿左角括號的字串裡反覆重試
元件端點位置 主專案 api/module_frame/__init__.py:POST /module-frame/import/verify(ModuleFrameImportDataVerifyRoute)→ app/module_frame/service/module_frame_import_service.py 的 verify_import_module_frame_data()(常數 HTML_TAG_PATTERN)
駭客怎麼打 ① 有建立範本權限的管理員;② 在檢核請求的條款欄送一格一百萬個左角括號的字串;③ 比對規則卡住,一個請求佔住處理程序兩分鐘;④ 四個請求卡住全部處理程序
得手什麼 用四個請求讓整個系統沒回應
修正的做法 ① 標籤比對規則改成「中間不得再出現角括號、最長 200 字」,不再反覆重試;② 條款或要求事項超過 2,000 字直接判不合格,沿用同檔的錯誤收集寫法、不丟例外
狀態 ✅ 已修(FR-114 CM-2181,主專案 commit 48638cbdb,1.21.0 出貨)。驗證:送一百萬個左角括號 0.07 秒內回判不合格
§31

M11-36 ⚪ 頁數極多的 CMMC PDF 讓解析時記憶體撐爆

欄位 內容
嚴重度 ⚪ 低(總表 #192;OWASP:A06 不安全的設計)
在哪裡 合規文件核心:原廠匯入 CMMC 合規框架 PDF
攻擊面位置 需要是平台管理員(框架是全域資源,服務層 require_platform_admin())
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
上傳的 PDF ↔︎ 解析程式。開檔後應該先擋總頁數、每頁讀完就釋放;當時頁數不設限,而且每頁讀完不關,記憶體隨頁數直線成長
元件端點位置 主專案 api/oscal/__init__.py:POST /oscal-framework-parse-jobs/parse(FrameworkParseJobParseRoute)→ app/oscal/service/framework_parse_job_service.py → 套件 jedi_oscal_v2/infra/adapter/cmmc/cmmc2_lv2_parser_adapter.py;頁數檢查 infra/adapter/base_parser_adapter.py 的 _assert_page_count()
駭客怎麼打 ① 平台管理員帳號;② 上傳一份 3MB、兩萬頁的 PDF 當框架;③ 解析時記憶體直線成長,吃到 3.4GB;④ 處理程序被砍
得手什麼 讓解析程序記憶體耗盡
修正的做法 ① 新增頁數上限 3,000 頁,開檔後先擋,超過回 400(OSCAL_V2_400006);② 兩輪取頁每頁用完就關閉釋放
狀態 ✅ 已修(FR-114 CM-2184,套件 commit ae5ffff5,1.21.0 出貨)。驗證:兩萬頁 3.26MB 修前 7.1 秒/973MB、修後 1.6 秒拒絕/167MB;2,999 頁照常解析;真實 CMMC 修前修後解析結果逐位元組相同
§32

M11-37 ⚪ PDF 裡一行超長的字讓「是不是目錄」的比對卡住

欄位 內容
嚴重度 ⚪ 低(總表 #193;OWASP:A06 不安全的設計)
在哪裡 合規文件核心:CMMC PDF 解析時判斷「這一行是不是目錄」
攻擊面位置 需要是平台管理員,同 M11-36
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
PDF 內容 ↔︎ 比對規則。判斷目錄的規則與取「[SELECT FROM:」標記後文字的規則遇到超長的一行會反覆重試;應該先限長度或改成不回頭的寫法
元件端點位置 同 M11-36 的 POST /oscal-framework-parse-jobs/parse → 套件 jedi_oscal_v2/infra/adapter/cmmc/cmmc2_lv2_parser_adapter.py 的 _is_toc_noise()
駭客怎麼打 ① 平台管理員帳號;② 做一份 0.04MB、五頁、每頁一行四萬字的 PDF;③ 上傳;④ 解析跑 52 秒,約十頁就超過逾時
得手什麼 佔住處理程序到逾時
修正的做法 ① 一行超過 500 字直接當內文、不跑目錄比對;② 取「[SELECT FROM:」標記後文字原本用「前面任意字+標記」的取代,改成逐一找出標記、取最後一個之後的字,語意相同、時間線性
狀態 ✅ 已修(FR-114 CM-2184,套件 commit ae5ffff5,1.21.0 出貨)。驗證:五頁每頁四萬字修前 52.3 秒、修後 4.4 秒
§33

M12-8 ⚪ 稽核計畫 Word 塞幾萬個空白,解析比對卡到逾時

欄位 內容
嚴重度 ⚪ 低(總表 #196;OWASP:A06 不安全的設計)
在哪裡 稽核輪次管理:稽核計畫 Word 解析器的三條比對規則(標題日期、時段、單位)
攻擊面位置 需要是某個專案的稽核員或管理者,上傳一份格式像樣的 Word
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
檔案內容 ↔︎ 比對規則。段落文字進比對之前應該先截長度;當時三條規則遇到大段空白會來回重試,長度加倍、時間變四倍
元件端點位置 主專案 POST /ap/{ap_uid}/docx-imports/parse(ApDocxImportUploadRoute)→ 套件 jedi_compliance_audit/app/service/ap_report_parser/airasia_cmmc_l1_v1.py
駭客怎麼打 ① 某專案的稽核員或管理者;② 在稽核計畫 Word 的標題或時段那一段塞幾萬個空白;③ 上傳;④ 2 萬字時標題日期規則就跑 20.7 秒,幾十 KB 的檔就能卡到逾時
得手什麼 佔住處理程序到逾時
修正的做法 ① 段落先截 2,000 字;② 時間與單位規則比對前把連續空白壓成一個;③ 標題日期只看標題最後 40 字
狀態 ✅ 已修(FR-114 CM-2181,套件 commit 2cb9d2cd、主專案 commit 48638cbdb,1.21.0 出貨)。驗證:標題與單位段各塞 2 萬空白,修前 5.77 秒、修後 0.01 秒,解析出的單位相同
§34

M24-16 ⚪ 首次安裝精靈設定碼塞中文或特殊字元,伺服器回 500

欄位 內容
嚴重度 ⚪ 低(總表 #214;OWASP:A10 例外狀況處理不當)
在哪裡 主系統:新機第一次開站的安裝精靈,要求輸入安裝程式印出來的設定碼
攻擊面位置 不需要帳號,但只在系統還沒開通時打得到(開通後這組入口就封了)。任何連得到這台新機的人都構成攻擊面
信任邊界位置 【連線 C02:瀏覽器經前門打 api;未登入的安裝精靈入口(經前門)】
未登入的外部 ↔︎ 開站入口。比對設定碼時應該對任何輸入都安全地回「不對」;當時用的比對函式遇到非英數字(中文、表情符號、網頁伺服器解出的高位字元)會直接拋錯,變成 500
元件端點位置 主專案 api/setup/__init__.py:GET /setup/status、POST /setup/provision(api/setup/routes/setup_route.py,X-Setup-Token 標頭)→ common/setup/setup_token.py 的 verify_token()
駭客怎麼打 ① 在客戶剛裝好、還沒開通的時候連到這台機器,不需要帳號;② 在設定碼標頭裡塞中文或特殊字元送出;③ 比對函式拋錯,伺服器回 500;④ 重複送,錯誤警報被灌滿,真正的錯誤被淹掉。不會放行、也不會洩漏設定碼
得手什麼 用假的錯誤警報淹沒真的錯誤
修正的做法 ① 比對前兩邊都先轉成位元組再比(固定時間比對不變);② 轉不過去(孤立的特殊字元)或型別不對一律回「不符」,維持失敗就拒絕。實際做法與模組頁原建議的「只收英數字」不同:改成「任何字元都能安全比對、不符就拒」,效果相同且不必另外維護字元白名單
狀態 ✅ 已修(8-E,CM-2215,commit 822216b6c,1.21.0 出貨)。驗證:帶「中文」「ÿéü」狀態查詢回 200、開通回 403,日誌 0 個錯誤堆疊
§35

M24-15 ⚪ 雲端硬碟通知說有去重複、實際沒做

欄位 內容
嚴重度 ⚪ 低(總表 #219;OWASP:A10 例外狀況處理不當)
在哪裡 主系統:Google 雲端硬碟有變動時推通知過來,系統排一個同步工作
攻擊面位置 對外開放的通知接收口,不帶使用者登入(是 Google 打進來),靠通道編號+通道密鑰比對才收。能送出有效通知的只有 Google 或握有密鑰的人
信任邊界位置 【連線 C14、C09:Google 通知進來,每則都排一個同步工作】
Google ↔︎ 我們後端。通知通過密鑰比對後,每一則都排一個同步工作;程式說明寫「工作端會依待辦狀態去重複」,實際排工作時沒有檢查是否已有同一租戶的待辦工作
元件端點位置 主專案 api/cloud_integration/__init__.py:POST /webhooks/google-drive/{tenant_id}(GoogleDriveWebhookRoute)→ app/cloud_integration/service/google_drive_webhook_service.py 的 handle_notification() → domain/cloud_integration/service/drive_sync_job_domain_service.py 的 enqueue()(直接新增一筆,不查重複)
駭客怎麼打 ①(多半是 Google 正常行為)雲端硬碟短時間內推來多則通知;② 每一則都通過驗證、各排一個同步工作;③ 同步多跑幾次。同步本身可以重跑、不會出錯,只多耗一點資源
得手什麼 沒有人得手;只是多跑幾次同步
修正的做法 不修的理由:重複通知只會讓同步多跑一次,同步本身可重跑、不會出錯,屬程式品質問題,不影響資料或權限
狀態 🚫 裁定不修
§36

M24-17 ⚪ 匯出診斷包時一邊帶時區一邊不帶,程式當掉回 500

欄位 內容
嚴重度 ⚪ 低(總表 #227;OWASP:A10 例外狀況處理不當)
在哪裡 主系統:客戶的平台管理員在畫面上選時間範圍、下載診斷包給原廠排查
攻擊面位置 需要登入且是平台管理員(服務層呼叫 require_platform_admin())。多半是正常操作踩到,不是攻擊
信任邊界位置 【連線 C02:瀏覽器經前門打 api】
使用者輸入 ↔︎ 服務層。兩個時間在比較前應該先統一格式;當時只帶起始時間(帶時區)、不帶結束時間時,程式自己補的結束時間不帶時區,兩者一比就拋錯變 500
元件端點位置 主專案 api/support/__init__.py:POST /support/diagnostic-bundle(DiagnosticBundleRoute)→ app/support/service/diag_bundle_export_service.py 的 _resolve_window()
駭客怎麼打 ①(正常操作就會踩到)平台管理員要匯出診斷包;② 只填起始時間、而且是帶時區的格式,結束時間留空;③ 程式比較兩個時間時拋錯,回 500;④ 診斷包匯不出來,排查被耽誤。不會外洩任何東西
得手什麼 沒有人得手;是維運拿不到診斷包
修正的做法 ① 比較前兩個時間都先過打包核心既有的 _as_naive_local(),統一成同一種格式(沿用同一支、不另寫);② 不合法的範圍照舊回 400、不夾成邊界值——靜默夾住會讓人以為抓了七天、實際只有一天
狀態 ✅ 已修(8-D,CM-2214,commit d39181f3b,1.21.0 出貨;同卡退回補修的 046553756 屬同卡另一條主機指令問題、與本條無關)。驗證:起始帶時區+結束不帶、結束帶 UTC+起始不帶,都正常回傳