---
title: 弱點檢測整合
eyebrow: Guidant AI 資安檢視 · 模組報告
h1: 弱點檢測整合（jedi-detection）
lede: 客戶買了「弱點掃描」之後，這塊負責保管掃描工具的帳號密碼、收下客戶上傳的檢測規則包、把掃描任務派到客戶機房。**這塊是全案最後收口的一塊**，2026-09-20 查完——找到兩條高風險，其中一條是「客戶上傳的規則包會被我們的伺服器當成程式碼執行」。
---

::: {.callout .warn}
**🔴 這一頁查完了，但「查完」不等於「查得乾淨」**

這塊是全案最後收口的一塊，2026-09-17 開始、2026-09-20 結束，總共分成 **14 批**查。

**為什麼要分這麼多批**：這塊的程式量大，而且一次交太多檔案給檢視工具，工具會中途把人砍掉、什麼都沒產出。所以一路往下切，切到每一批只剩幾個檔案為止。

**這 14 批裡有 4 批是在跟工具搏鬥，不是在查程式**——一批打了 17 小時、換 25 次人、零產出，一批的完成紀錄被誤刪，一批換了四次人，一批派三位只回來一位。**這些不是要藏起來的難堪，是全站在 2026-09-20 決定止損的依據**（見[總報告首頁](index.html)）：工具不穩到這個程度，繼續往下掃拿不回相稱的東西，所以整輪改成「針對已經發現的修」。細節寫在下面的軌跡表底下。

**所以這一頁該這樣讀**：下面列的每一條都是查證過、可以拿去排工單的；但**「這 14 批裡沒再找到別的」不等於「這塊已經沒有問題」**——我們的方法能證明「找到了什麼」，不能證明「沒有別的」。
:::

## 這塊在產品裡做什麼

客戶買了「弱點掃描」功能之後，他可以在產品裡對自己機房的主機做資安檢測——掃有沒有沒修的漏洞、設定有沒有照規範設好。

要做到這件事，這塊要處理四件事，**每一件都碰到高風險的東西**：

::: grid2
::: {.card .plain}
#### 它平常在做什麼
1. **保管客戶的掃描工具帳號密碼**（SonarQube、OpenVAS、ZAP 這些工具的存取權杖，還有連主機用的 SSH、Windows 遠端管理帳密）
2. **收下客戶上傳的「檢測規則包」**——一包壓縮檔，裡面是「要檢查哪些項目」的規則
3. **把掃描任務派給裝在客戶機房的代理程式**，並把帳密一起帶過去
4. **把掃描報告收回來，存成稽核證據**
:::
::: {.card .crit}
#### 為什麼它是敏感目標
這塊同時碰到四件高風險的事：

- 手上有**客戶主機的明文帳號密碼**
- 要**解開客戶上傳的壓縮檔**
- 要**代替客戶去抓外部網址**
- 要**執行外部程式**來解析規則包

前面提過的「不用登入就能取走客戶帳密」那條攻擊鏈，**終點就在這裡**——帳密解密之後放進派工單的那一行。
:::
:::

---

## 檢視軌跡

**檢視期間**：2026-09-17 ～ 2026-09-23（**已收口**）｜**範圍**：模組本體正式程式碼 160 個檔案，扣掉另一條線已查過的 11 個、以及 66 個沒有邏輯的檔案（空殼、純資料結構、離線小工具），**實際檢視 83 個**；另加我們主系統這一端的接線 **12 個**｜**共 15 批**（模組本體 14 批＋主系統接線 1 批）

⚠️ **模組本身那 83 個檔案、以及我們主系統這一端的 12 個接線檔都查完了**。接線那一批的原始報告放在 [FR-115 站](https://guidantai-feature-doc.jedicotech.com/FR-115-2609-host-wiring-security-scan/)（五支模組的接線放在同一個站）。

⚠️ **下表 13 列，但上面說 14 批**——差的那一批是 D2-2：它打了 17 小時、一條都沒產出，後來整批重切重跑。**它沒有結果可以列**，所以不進表，只寫在表底下的誠實交代裡。

> **原始技術報告**放在需求中心的 [FR-108 站](https://guidantai-feature-doc.jedicotech.com/FR-108-2609-detection-security-scan/)，檔名見下表最後一欄。那裡有每一批的完整技術細節、檔案清單與逐條推理，供需要深究或稽核抽查時查閱。

| 批次 | 查什麼 | 檔數 | 覆核投票 | 結果 | 原始報告檔名 |
|---|---|---:|---|---|---|
| **D1** | 工具名錄、客戶帳密怎麼存、「測試連線」怎麼運作 | 36 | 18 票全投完（⚠️ 完成證明遺失，見下） | 找到 4 條（**1 高** 3 中） | `scan-D1-tools-credentials` |
| **D3-1** | 代理程式回報結果、報告檔怎麼存成證據 | 4 | 9 票全投完 | 找到 1 條中 | `scan-D3-1-result-collection` |
| **D3-2** | 派工時「要掃哪些機器」怎麼被綁定 | 5 | 6 票全投完 | 找到 1 條中 | `scan-D3-2-job-binding` |
| **D3-3** | 執行狀態怎麼流轉、查詢入口 | 11 | 3 票全投完 | **這一批沒有新問題**（工具唯一報的那條是已知舊帳） | `scan-D3-3-state-machine` |
| **D3-4a** | 派工／取消／刪除 | 1 | 9 票全投完 | 找到 1 條中 | `scan-D3-4a-orchestration-dispatch` |
| **D3-4b** | 回收／狀態／排程 | 1 | 9 票全投完 | **淨新增 0**，三條全是舊帳；但**把另一條舊問題的影響面擴大了** | `scan-D3-4b-orchestration-collect` |
| **D2-1b** | 上傳的壓縮檔怎麼驗 | 2 | 6 票全投完 | 找到 1 條中，**並推翻了工作單的一項前提** | `scan-D2-1b-archive-validation` |
| **D2-1a** | 規則包上傳入口的守門 | 3 | 6 票全投完，零駁回 | 找到 2 條中（⚠️ 這一批跑得很不順，見下） | `scan-D2-1a-upload-route` |
| **D2-3** | 規則包怎麼解析、怎麼去外部抓 | 4 | 6 票全投完，零駁回 | 🔴 **找到 2 條，其中一條是本塊第二條高風險** | `scan-D2-3-extraction-fetch` |
| **D2-2a** | 掃描基準的主要邏輯 | 1 | 3 票全投完，零駁回 | 找到 1 條中（另帶出兩條體質項） | `scan-D2-2a-profile-service` |
| **D2-4a** | 分類字典的完整路徑 | 8 | **一個疑點都沒提** | **範圍內沒有找到問題**（另記一條體質項） | `scan-D2-4a-taxonomy-chain` |
| **D2-2b** | 掃描基準的資料存取層 | 4 | **一個疑點都沒提** | **範圍內沒有找到問題**（另記一條體質項） | `scan-D2-2b-profile-domain-repo` |
| **D2-4b** | 版本存取層、規則清單表、掃描範圍的寫法 | 8 | 1 條提出、3 票全投完 | **淨新增 0**——唯一提出的那條與第 11 條是同一條問題鏈（⚠️ 這一批跑得很不順，見下） | `scan-D2-4b-version-repo` |
| **W3**（主系統接線） | 我們主系統這一端怎麼把這塊接上來：設定、上傳入口、派工與權限檢查 | 12 | 一個疑點都沒提（完成證明齊全） | **範圍內沒有新問題**；執行者自己開檔另查出 1 條非資安的設定問題（見下方體質問題最後一列），並替第 7 條加了一個驗收項 | `scan-W3`（在 FR-115 站） |

::: {.callout .warn}
**四件跑不順的事——這就是我們決定止損的依據**

下面四件不是「做得不順要老實承認」而已。**它們是 2026-09-20 那個決定的證據**：全站在這一天判斷「檢視工具本身不可靠，繼續投入不划算」，改成針對已經發現的修（見[總報告首頁](index.html)）。這一塊剛好把工具的不穩定度量得最清楚，所以細節列在這裡。

**一、第一批（D1）的完成證明拿不出來。** 產物資料夾被另一個被中斷的檢視程序誤刪了（**不是同時跑太多造成的**）。統籌者改從工作流程的結果檔獨立核完——6 個疑點、18 票全投、零漏投零中斷零降級。**證據鏈補得起來，但那份完成紀錄確實沒了。**

**二、有一批（D2-2）打了 17 小時、換了 25 次人，一條都沒產出。** 統籌者逐份看過那 25 份紀錄：22 份只寫了開頭一句就被中斷、3 份寫到一半。根因是工具自身的一個已知缺陷（上游還沒修），**不是內容太難**。**後來把它拆成兩小批重跑，兩批都順利完成。**

**三、有一批（D2-1a）跑得很不順**——換了四次人，前三次都被誤中斷，耗時 9 小時 22 分。**那一批的完成紀錄只能證明「被提出的兩條投票完整」，不能證明「三個檔案被徹底讀過」。**

**四、最後一批（D2-4b）也不順**——派了三位、只有一位跑完，前兩位被工具自身的一個自動壓縮機制誤砍（與第二點是同一個上游缺陷），整批耗時 4 小時 53 分。**那位跑完的人交出的紀錄是完整的**（提出 1 條、三票全投完、統籌者另把工作單列的三個重點逐項開檔查證），**但另外兩位讀到哪裡，沒有紀錄可以還原。**

**把這四件擺在一起看**：14 批裡有 4 批的成本花在「讓工具跑完」而不是「查程式」，其中最糟的一批投了 17 小時、拿回零條。**這個比例就是繼續投入不划算的理由**——不是我們怕麻煩，是同樣的時間放到修正那一側，拿得回的東西明確得多。
:::

::: {.callout .ok}
**兩批「零發現」是有份量的零，不是空手而歸**

其中兩批（D2-4a、D2-2b）**一個疑點都沒提出來**。這種情形要分兩種看：

- **一種是「沒讀就被中斷」**（像上面那 25 位）
- **另一種是「讀完了、逐條交代每個懷疑過的地方為什麼不成立」**

**這兩批是後者。** 而且統籌者把工作單列的重點逐項開檔查證、連開發環境核對資料庫規則與連線帳號權限，全部乾淨。**最後一批（D2-4b）雖然提出了一條，但那條查下來與第 11 條是同一條問題鏈**——由兩組不同的人、用不同的切法各自獨立撞到同一件事，反而讓第 11 條更站得住腳。

判準是看紀錄裡有沒有「結論」這個事件——**「零條」有兩種，可信度天差地別。**
:::

---

## 問題一覽

**排序按風險，同風險的同一類排在一起。**「出事會怎樣」那欄講的是**業務影響**——誰受影響、損失什麼，不是技術現象。

⚠️ **表裡有 14 列，但「這塊自己找到的」是前 13 條**（2 條高風險、10 條中風險、1 條低風險）。

⚠️ **第 8 條重新定性後已從中風險調降為低風險**（原因寫在那一列）。表格原則上按風險排序，但**第 8 條保留在原位置沒有往下移**，因為報告其他地方與工單都用「第 8 條」這個編號在指它，移動會對不上。

⚠️ **第 14 條不是這塊的新發現**，是別的模組早就登記過的一個問題，這次查出它的波及範圍也涵蓋到這塊——**條數不重複計算，但修的時候不能漏掉這塊**。詳見該列與下方結論第 7 點。

| # | 問題 | 風險 | 分類 | 出事會怎樣（誰受影響） | 怎麼修 | 狀態 |
|---|---|---|---|---|---|---|
| **1** | 🔴 **客戶上傳的檢測規則包會被外部工具當成程式碼直接執行** | 🟠 高 | 外部送什麼就收什麼 | **拿到的是我們後端主機的全部權限**——一整批系統密鑰（含用來簽發登入憑證的那一把，拿到就能冒充任何人登入，另有代理程式的憑證私鑰、檢測工具的加密金鑰與一整排外部服務的通行證）、一個**能自行把自己提權成超級管理員**的資料庫身分（提權之後讀寫**全部客戶**的資料）、檔案儲存與代理程式的憑證，以及跳進內網的能力。**一個客戶的管理員就能打下整台主機與全平台資料** | 重新打包之前先讀一次規則包裡的設定檔，看到程式樣板標記就拒收；**要放在打包那一步本身**，只補上傳那條路會漏掉網址那條。實際做法：解析前先讀 `inspec.yml`，含 `<%` 樣板記號直接拒收（只擋這一個檔，見打折說明） | ✅ **已修（CM-2057）** |
| **2** | 🔴 **「測試連線」按鈕沒有檢查權限，只要能登入就能按——按下去系統會把掃描工具的帳密解密，送到按的人自己指定的那台主機** | 🟠 高 | 只驗登入、不檢查歸屬 | **一次拿走整家公司的維運帳密**——送出去的是連主機用的 SSH／Windows 遠端管理密碼，以及三套掃描工具的存取權杖。**這是漏掉不是刻意**：同一支檔案裡的新增、修改、重置每一支都有檢查權限，**真正的缺口只有「測試連線」這一支**（同樣沒掛的「查引用次數」是純查詢、不解密、不對外連線，沒掛是合理的）。⚠️ **既然第 6 條裁定不限制連到哪裡，這道權限檢查就是唯一的防線** | 補一行權限宣告，同一支檔案裡有三處現成寫法可以照抄 | ✅ **已修（CM-2042）** |
| **3** | **網址型規則來源完全繞過壓縮檔的檢查** | 🟡 中 | 外部送什麼就收什麼 | 防壓縮檔炸彈的三道上限（檔案數／大小／膨脹倍數）**只接在「上傳檔案」那條路上**；改填一個網址，下載回來直接解開，三道上限一道都不會碰到。**45MB 的檔案解開成數十 GB 完全做得到**，伺服器記憶體被吃爆、所有客戶一起停擺 | 把檢查改放到解析那一層，讓現有與未來的第三種來源天然都涵蓋。實際做法：三道上限下沉到上傳與網址共用的 `_read_entries()`，不在網址分支補第二套 | ✅ **已修（CM-2057）** |
| **4** | **上傳規則包的「最多一萬個檔」上限，對 `.zip` 格式形同虛設** | 🟡 中 | 資源耗盡 | 上限要等檔案清單全部展開進記憶體之後才開始數——**數到第一萬零一個才喊停時，記憶體早就吃掉了**。實測：一個 49.7MB、裝 59.5 萬個空檔的壓縮檔，光打開就多吃 360MB 記憶體，連送幾次就能把伺服器打到停擺。**最難察覺的是日誌看起來一切正常**（照樣回報「壓縮檔不安全」），但代價已經付掉了 | 打開之前先讀壓縮檔檔尾的目錄筆數、超量直接擋；**並且改掉那段寫反的說明文字**。實際做法：打開 zip 前先讀檔尾 EOCD 筆數欄位擋量，並改掉寫反的註解 | ✅ **已修（CM-2057）** |
| **5** | **填網址建立掃描基準時系統放行沒加密的連線，而且網址型來源完全不記指紋** | 🟡 中 | 外部送什麼就收什麼 | 代理程式住在客戶內網、手上有主機帳密，它拿到網址後直接下載並**執行**裡面的規則。**路徑上任何能動手腳的人換掉那包檔案，就等於在代理程式裡跑自己的程式碼**，掃描報告也跟著造假。**沒有加密要破解，因為根本沒有加密**——而我們後端自己的下載器只准加密連線，同一個系統兩套標準 | 只收加密連線；第一次解析成功時記下檔案指紋並讓代理程式對帳（**上傳那條路本來就這樣做，網址這條只是沒補上**）。實際做法：新登記只收 https、既有 http 留警告日誌、網址型落 sha256 並下發給代理程式對帳 | ✅ **已修（CM-2057）** |
| **6** | **「測試連線」要連到哪台主機，是呼叫者在請求裡直接指定的** | 🟡 中 | 外部送什麼就收什麼 | **客戶內網裡的代理程式因此變成「只要登入就能用的任意連線跳板」**——回應訊息的差異（連得上／連不上／逾時）還能拿來一台一台探測客戶內網有哪些機器開著哪些服務。⚠️ **與第 2 條是同一個入口上的兩個獨立問題**：第 2 條是「誰可以按」，這條是「按下去可以打到哪」 | **不限制可以連到哪裡**（已裁定不做目標白名單——掃描目標是客戶自己的主機、帳號也是客戶提供的，哪台算合法目標該由客戶自己管）。改做三件：① **限制單次可帶的主機數量**，擋的是「一個請求帶一萬台就變成內網掃描器」；② **回應收斂成單純的成功／失敗**，擋的是「用連得上／連不上／逾時的差異一台一台探測」；③ **日誌要記誰按的、連到哪一台、用了哪一組設定**——只記「有人按了測試連線」追不到事。實際做法：主機數上限＋回應收斂＋稽核 log | ✅ **已修（CM-2058）** |
| **7** | **資料庫的客戶隔離規則方向寫反了** | 🟡 中 | 客戶資料沒隔開 | **子單位的人看得到母單位的資料（還能改、能刪），母單位反而看不到自己底下子單位的資料**——隔離該擋的沒擋、不該擋的擋了，**資安與功能兩面同時出錯**。**同樣的寫法在出貨基線裡共有 11 條**（本塊五張表、遠端代理程式一張、授權三張，再加**合規文件匯入那塊的兩張**），**只改這五張等於沒修**。⚠️ **合規文件匯入那兩張是第三種寫法、比另外九條更寬鬆**——它用的是純文字包含比對，單位編號 1 會命中任何含有 1 的路徑；而且它拿單位編號去跟使用者編號比對，**這兩者根本不是同一種東西**。用同一個關鍵字搜尋找不到它們，**要單獨處理**。跟第 2 條疊起來更糟：子單位成員可以對母單位登記的工具按「測試連線」，把母單位的帳密送到自己的主機 | 🔴 **11 條要分兩組改，不是全部照同一套**：**8 張**改用同一個檔案裡已經在用的正確寫法；**授權那 3 張**（授權檔、授權異動紀錄、停權紀錄）**「看」要保持現狀、只擋「改」和「刪」**——**那三張絕對不可以照另外八張的做法改**，改了每個子單位都會查不到頭上那張授權，被判成「沒買」，**打任何功能都會被擋掉**（原因見[授權管理那塊](M18-license.html)）。應用層也要補上客戶條件。**已裁定集中處理**：與其他修正工單一起做，不為此單獨重新產生出貨基線（產品還沒出貨，「新裝客戶拿到錯的」目前不存在；11 條分批改等於改兩次驗兩次，漏一張就白做）。🔴 **但出貨前必須完成**——第一個客戶裝機那一刻，代價就從零變成實質。🔴 **施工時逐條比對、不要批次取代**：這些跟基線裡上百條正確的混在同一個檔案，批次取代會把對的一起改壞 | ⬜ **未修，歸 1.21.1 hotfix**（CM-2289） |
| **8** | **權限矩陣騙了管理員——取消勾選後前端選單藏起來了，後端八支讀取功能照樣回全部** | ⚪ 低 | 只驗登入、不檢查歸屬 | ⚠️ **這條原本評中風險，重新定性後調降為低。** 原本的寫法會讓人以為「任何登入者都拿得到」，**但這個功能本來就只開給租戶管理員使用**，拿得到的人本來就拿得到。**真正的問題是管理員被騙了**：他在權限矩陣裡取消勾選，前端選單確實藏起來，但該帳號直接對系統送請求，後端八支讀取功能（基準清單、下拉選單、單筆詳情、版本歷史、主檔使用狀況、版本使用狀況、規則清單、抽取狀態）一支都沒檢查，照樣回全部。**也就是說那一格勾選其實不生效** | 八支讀取功能補掛權限檢查，**並保留那顆權限點**以維持未來細分的彈性（例如日後讓稽核人員「看得到但改不了」）。🔴 **補的時候要一併把程式裡那句「讀取端刻意不掛」的說明文字改掉**，否則下一個人看到又會當成設計、把它拿掉。🔴 **這個功能有公版機制，不要改壞**——看得到哪些資料是三種用「或」連起來（公版人人看得到＋自己建的＋上游分享下來的），補權限是加在「能不能進這個功能」那一層，不動「看得到哪些」那一層；**補完要驗四件事**：管理員看得到公版、看得到自己的、下游看得到上游分享的、沒勾權限的被擋掉。**「改壞」的長相是「有些本來看得到的人突然看不到了」** | ✅ **已修（CM-2042）** |
| **9** | **按下「開始掃描」時，解密後的客戶主機帳密會多存一份明文進派工資料表** | 🟡 中 | 密碼外流 | **那份存下來的從頭到尾沒有任何程式讀過**——代理程式拿到的是另一份當場重新解密的。等於產品刻意做的加密保護被這一行繞過：**拿到資料庫備份或唯讀帳號的人，一句查詢就得到客戶正式機房的可用主機密碼與掃描器管理員權杖**。而且這張表沒有任何清理機制，**每掃一次就多留一份、無限累積** | 刪掉那一行寫入，再補一支清掉存量的腳本（已確認刪掉不影響代理程式）——**已刪除 `_dispatch_one()` 裡寫入明文的那一行，正常派工與「立即重新指派／重跑」兩條路徑共用同一函式，一次刪除兩條路都修好；DEV 查過既有殘留資料筆數為 0，無需補清理** | ✅ **已修（FR-114.4-3）** |
| **10** | **手動重新解析功能無條件開一條背景工作，可以把主機打掛** | 🟡 中 | 資源耗盡 | 每收一次請求就開一條背景工作、把最大 50MB 的檔整包讀進記憶體、再開一支最長 15 分鐘的外部程式，**沒有同時執行上限、也不檢查這一版是不是已經在跑**——連打幾百次就是幾百條同時存在，**同一台機器上所有客戶一起變慢** | 加「同一版已在跑就拒絕」的判斷＋總量上限。⚠️ **不只是「加一個判斷」而已**——手動重新解析目前會**主動把狀態壓回「等待中」再排**（當初這樣寫是怕前端看到舊的失敗狀態、以為按了沒反應）。**這個壓回的動作要一起處理，否則新加的判斷永遠看到「等待中」、擋不到任何東西。修改範圍比表面上大**。實際做法：同版去重＋同時上限 4＋排隊上限 | ✅ **已修（CM-2058）** |
| **11** | **換掃描工具時，「要掃哪些機器」不會拿實際生效的值重新檢查台數上限** | 🟡 中 | 外部送什麼就收什麼 | 先綁一支不設台數上限的工具、把範圍填成一個超大網段；再送第二次只換工具不帶範圍——**舊的超大範圍就原封不動留著跟到新工具底下**。按下執行時系統會把網段逐台展開，**一個大網段展開是 1,677 萬台**，記憶體瞬間吃爆、服務當掉。統籌者驗收時另外查到**同一個洞的第二個發生點** | 用「這次更新完成後實際會生效的值」重新檢查；展開網段前先加一道很寬鬆的總數上限當保險。實際做法：用生效值重驗＋展開端絕對上限 65536 | ✅ **已修（CM-2058）** |
| **12** | **查掃描執行紀錄少了「你是不是這個專案的人」這道檢查** | 🟡 中 | 只驗登入、不檢查歸屬 | **同一家客戶裡任何成員知道任務編號，就讀得到別人專案的掃描歷史**。同一個服務裡另外八個吃任務編號的功能每一個都先做了這道檢查，**只有這一支漏掉** | 補上同一個服務裡其他八處已經在用的那行檢查；任務不存在時回報找不到，不要回空清單 | ✅ **已修（CM-2040）** |
| **13** | **代理程式回報報告時「檔名叫什麼就存什麼」** | 🟡 中 | 外部送什麼就收什麼 | 不去掉路徑、也不驗副檔名。稽核人員在畫面上點開**預覽**時，**網頁格式的檔案會被瀏覽器當成網頁執行**——那段程式是用**稽核人員本人的身分**在跑，可以冒用他做任何事。**而網頁格式正是掃描報告的正式格式，這是日常路徑不是罕見情境**。⚠️ **要走到危險的那個動作得多轉一手**：在「執行歷史」那一區點掃描報告是**下載**（強制存檔、不會在頁面裡開，那條是安全的）；危險的**預覽**要從**證據清單**那邊點。掃描報告收回來時**會同時寫一筆證據**，所以這條路是通的 | 存檔前先去掉路徑、再比對副檔名白名單；更保險的做法是一律自己命名。實際做法：報告檔名一律去路徑＋查副檔名白名單，不合規退回系統自產名，中文檔名保留 | ✅ **已修（CM-2062）** |
| **14**<br>⚠️ **不計入本塊條數** | **只能看、沒有待辦任務的「唯讀角色」，其實可以對客戶的正式機器發動帶帳密的掃描**<br>**（這條是「稽核流程」那塊已登記問題的波及範圍，不是本塊的新發現）** | 🟡 中 | 只驗登入、不檢查歸屬 | 這一塊有八支會改狀態的功能（開始掃描、取消、重跑、刪紀錄等）**吃的是同一道「任一角色都放行」的守門**。決策者已裁定唯讀角色不該能動別人的任務——**已修：八支全數改接嚴格守門，只有該任務的被指派人或專案 manager 能操作** | 補「呼叫者是不是這個任務的負責人、或這個專案的管理者」這道檢查，**範圍涵蓋這八支**——**流程那邊的嚴格守門已做好（FR-114.1-1，`common/authz/` 的 `assert_workflow_job_operator`）；套件側八處呼叫點已改接 `WorkflowExecutionService.assert_job_operator(workflow_execution_id, template_job_id)`（FR-114.1-2），背景自動完成路徑（無 request context）維持原樣不掛守門** | ✅ **已修（FR-114.1-2）** |

### 另外記下的七條體質問題

這七條**都不是現在打得到的漏洞**。多數是「目前沒事是巧合、不是設計」的地方，列出來是因為它們會長成漏洞；其中兩條查下來另有結論——一條的危害情境根本不成立、一條是產品刻意的行為決定，兩條都在下表標明了。

**最右欄是各自的處置**——它們不是同一種待辦，有的跟著這塊的修正一起做，有的只改註解不動程式，有的只是記錄。

| 問題 | 為什麼現在沒事 | 為什麼還是要記 | 怎麼處置 |
|---|---|---|---|
| **「分享」這個範圍值，三層各認一套** | 建立掃描基準時填不出「分享」（被擋），但**改基準時卻改得成**——資料庫認、存取層認、業務邏輯那層不認 | 一個欄位三個地方各認一套值域，**目前沒壞是巧合** | ✅ **跟著這塊的修正一起做**——既然要動這塊，順手把三個地方對齊 |
| **模組自帶的兩支資料庫腳本都落後主線**（建表的那支、設定隔離規則的那支） | 安裝程式會**先跑出貨基線**（已經是新的），之後才跑模組自帶那一輪，而那些腳本都是「已存在就跳過」——**表已經是新的，那一輪等於空轉**。所以「新裝客戶會出事」這個情境不會發生 | 真正的問題不是資安，是**這個模組宣稱自己可以獨立安裝，而這個宣稱是假的**。會受害的是「不走我們安裝程式、只拿這個模組自己裝」的人——**今天沒有這種人** | 📝 **只記錄，不處理**（情境不成立） |
| **一段說明文字寫的跟資料庫實際規則相反** | 說「刻意不受隔離、要看到全站」，實際上受隔離管；能走到這一步的人本來就是平台管理員，殊途同歸 | 那段文字**前半句說「不受隔離」、後半句自己又承認「仍受規則擋」，前後矛盾，而事實是後半句對**——結論碰巧正確、推理是錯的。**下一個人只讀前半句就會放心開放刪除功能，而那會把數量誤判成 0、把還在用的標籤刪掉** | 📝 **跟著一起改掉那段文字，程式不動** |
| **掃描基準那四個檔裡，一行客戶隔離的程式碼都沒有**——安全完全外包給資料庫 | 三個前提目前逐項查過都成立：資料庫隔離規則正確、連線帳號沒有繞過權限的特權、上層一定會先經過受保護的資料表 | **三件事任何一件被改動，這四個檔不會有任何反應**——不報錯、測試不會變紅，只會安靜地開始回傳別的客戶的資料 | 📝 **把這三個前提寫進那幾個檔的開頭說明，程式不動**。⚠️ **在這層自己再算一次不是正解**——那會變成「兩套規則各自維護」，更壞。要留的是那句前提提醒 |
| **規則清單那張表的保護「靠約定、沒有機制強制」** | 這張表沒掛資料庫隔離，靠的是「要查它一定得先拿到版本編號，而版本編號只能從受保護的表取得」；現有三個入口逐條追過，確實都先經過那一關 | 取資料那支方法是公開的，**自己不檢查手上的版本編號是不是這家客戶的**——**日後第四個入口只要手上有編號就能直接繞過，不會有任何錯誤訊息，只會悄悄拿到別家客戶的規則清單** | 📝 **把這個前提寫進該檔開頭說明，程式不動**（理由同上一列） |
| **共用的分頁查詢工具，把跨欄位的條件用「或」而不是「且」連起來** | 不構成跨客戶外洩（客戶隔離靠資料庫那層擋，不靠這層條件） | ⚠️ **這是刻意設計、不是小 bug**——那支共用工具的說明文字明寫「多個文字條件以『或』連接」，實作也對得上。所以**不能當小 bug 順手修**：改它等於改全產品共用底層的對外約定，至少五支模組在用，沒特別動它的一律默默吃這個預設行為。影響**不是外洩**，是**正確性**——同時填兩個文字條件，拿回來的是「符合其中一個」，而使用者以為自己篩過了 | 🗂️ **另開獨立工單處理，本塊只記錄、不列為待修**。這是**產品行為決定，不是資安問題** |
| **規則包解壓上限有三套數字，其中一套根本沒接上**（主系統接線那批查出） | 三套數字現在剛好一致，所以行為正常；沒接上的那套只是擺著，不會被讀 | **哪天有人以為改那套就能調上限，改了卻完全沒效果**——而且三套各自演化，下次數字就不一致了 | 🗂️ **非資安，另開一般修正工單**：收成一套、刪掉沒接上的那套 |

---

::: {.callout .plain}
**下面只展開需要你自己判斷、或容易被誤解的幾條。** 其餘的修法明確，看上面表格的「怎麼修」那一欄就夠了——**沒有展開不代表沒查，也不代表不重要。**
:::

## 第 1 條：上傳一包規則，就在我們的伺服器上執行 {#rce}

::: {.callout .crit}
**這是本塊影響面最大的一條。**

不是「多看到一些資料」，是**整台後端主機交出去**。
:::

### 問題是什麼

客戶要做檢測，得先給系統一包「檢測規則」——一個壓縮檔，裡面有一支設定檔說明這包規則是什麼。

系統把這個壓縮檔**原封不動交給一支外部工具**去解析。問題在這支外部工具身上：**它讀那支設定檔的時候，會先把檔案內容當成程式樣板跑一遍，再拿結果去解析。**

這是那支工具自己的行為（統籌者打開本機安裝的那份原始碼逐行核對，確認一字不差），**不是我們寫錯**——但我們把使用者的檔案直接餵了進去。

**而且不只這一條路。** 同一支工具還有第二種讀法：**如果設定檔用的是「程式檔」那種形式，它會直接把整個檔案當程式碼執行**——比樣板那條更直白，連包裝都不用。兩條路的結局一樣，防法也一樣（在交給它之前先看內容），列出來是為了讓修的人知道不能只擋樣板那一種。

### 我們的驗證器為什麼擋不住

驗證器自己在檔頭寫明：**「只驗結構不驗內容」**。它檢查——

| 檢查了什麼 | 有沒有看設定檔的內容 |
|---|---|
| 副檔名、檔案大小 | ❌ |
| 檔案格式的識別碼 | ❌ |
| 檔名會不會解壓到別的目錄 | ❌ |
| 有沒有捷徑檔 | ❌ |
| 檔案數量、膨脹倍數 | ❌ |

**沒有一項在看那支設定檔裡面寫了什麼。**

所以**一份檔名正常、結構正常的惡意規則包，一道防線都不會碰到**。建好基準之後系統自動排解析，程式就執行了。

### 打進去能拿到什麼

| 拿到的東西 | 能做什麼 |
|---|---|
| **一整批系統密鑰** | 除了簽發登入憑證那一把（拿到就能**冒充任何人登入**，包含平台管理員），還有**代理程式的憑證私鑰**、檢測工具的加密金鑰，以及一整排外部服務的通行證 |
| **能自行提權的資料庫身分** | 這個帳號本身**沒有**繞過客戶隔離的特權，但它**可以自己把「我是超級管理員」那個開關打開**，而全庫的隔離規則都認那個開關。**結果一樣是讀寫全部客戶的資料**——但要講清楚是「能自行提權」，不是「本來就是管理員」 |
| 檔案儲存與代理程式的憑證 | 取走所有客戶上傳的檔案；接管客戶機房的代理程式 |
| 那台主機本身 | 當成跳板往內網走 |

**一個客戶的管理員，就能打下整台主機與全平台資料。**

而且**網址型來源更省事**——把同一份檔案放自己的伺服器上填進去即可，**連上傳都不用**。

### 為什麼評「高」而不是「最嚴重」

| 判斷依據 | 這件事 |
|---|---|
| 要先有帳號嗎 | **要**，必須是登入的客戶管理員 |
| 過了那一道之後呢 | **沒有第二道防線** |

**門檻只有這一道。**（對照：全案唯一那條「最嚴重」是連帳號都不用的。）

### 建議怎麼修

| 步驟 | 做什麼 | 為什麼不能省 |
|---|---|---|
| **一** | 重新打包之前先讀一次那支設定檔，**看到程式樣板標記就拒收** | 這是唯一在所有來源之前的共同關卡 |
| **二** | 這道檢查要放進**打包那一步本身** | **放在上傳那條路的服務層會漏掉網址那條路**——兩條路共用同一支打包程式 |
| **三**（長期） | 把那支外部工具關進沙箱跑 | 就算哪天又冒出第三條路，被執行的程式也出不來 |

---

## 第 2 條：一個按鈕，送走整家公司的維運帳密 {#test-connection}

### 問題是什麼

掃描工具設定頁上有個「測試連線」按鈕，本來是給管理員確認「我填的帳密對不對、連得上嗎」。

按下去之後，系統會**把存好的帳密解密，交給裝在客戶機房的代理程式，送到指定的那台主機**去試連。

**問題是這個按鈕沒有檢查權限**——任何一個能登入的帳號都按得動。

### 這是漏掉，不是刻意

| 同一支檔案裡的功能 | 有沒有檢查權限 |
|---|---|
| 新增工具設定 | ✅ |
| 修改工具設定 | ✅ |
| 重置工具設定 | ✅ |
| **測試連線** | ❌ ← **真正的缺口** |
| 查引用次數 | ❌（但這支沒掛是合理的，見下） |

**本報告撰寫時開檔核對，三支寫入功能的權限檢查都在，那兩支仍然只有「客戶有沒有買這個功能」這一道。**

**但這兩支的性質差很多，要修的只有一支**：「查引用次數」是**純查詢**——不解密任何東西、也不對外連線，沒掛權限是合理的。「測試連線」雖然名字像查詢，**實際是寫入性質**：它會實際把憑證送出去，還會回寫測試結果。**只有這一支是缺口。**

### 送出去的是什麼

連主機用的 SSH 密碼、Windows 遠端管理帳密，以及 SonarQube、OpenVAS、ZAP 這些掃描工具的存取權杖——**等於一次拿走整家公司的維運帳密**。

::: {.callout .warn}
**這條跟「設備清冊讀取不守」那條長得像，但不能比照放行**

那一條是**純讀取**，這一條會**解密憑證並主動對外連線**。性質完全不同。
:::

### 而且「連到哪裡」也是呼叫者說了算（第 6 條）

要連到哪台主機，是呼叫者在請求裡直接指定的——**系統既不比對白名單，也不管這台主機跟存好的設定有沒有關係。**

結果是客戶內網裡那支代理程式變成**「只要登入就能用的任意連線跳板」**，而且回應訊息的差異（連得上／連不上／逾時）還能拿來一台一台探測客戶內網。

::: {.callout .decided}
**已裁定：不做目標白名單**

原本的建議是「只准連到該客戶已經登記的掃描目標」。**這個建議已經撤掉。**

**理由**：掃描目標是客戶自己的主機、連線帳號也是客戶提供的，**我們無從判斷哪一台算合法目標——這件事該由客戶自己管**。硬做白名單只會擋掉正常使用：測連線本來就常常是在「還沒登記的主機」上測。

**改做三件**：

1. **限制單次可以帶幾台主機**——擋的是「一個請求帶一萬台，測試連線就變成內網掃描器」
2. **回應收斂成單純的成功／失敗**——擋的是「用連得上／連不上／逾時的差異，一台一台探測客戶內網」
3. **日誌要記三樣才追得到**：誰按的、連到哪一台、用了哪一組設定。**只記「有人按了測試連線」沒有用**

前兩件都不會擋到正常使用——真的在測連線的人一次就測一台，也只需要知道「這組帳密能不能用」。
:::

🔴 **既然不限制連到哪裡，第 2 條那道權限檢查就是唯一的防線。** 第 2 條原本被歸在「跟第 6 條一起修才有意義」，現在它自己就是那個意義——**第 2 條要往前排**。

---

## 這塊的結論

::: {.callout .crit}
**一句話**：**這塊查完了，找到十三條，其中兩條高風險**（另有一條是別處問題波及到這裡）。這兩條高風險的共同特徵是——**這塊手上握著的東西，本來就是全案最敏感的**（客戶主機的明文帳密、可執行的規則包、對客戶內網的連線能力）。

**建議的處理順序**：

1. **第 1 條最優先。** 它是目前本塊唯一一條「一次得手就拿到整台主機與全平台資料」的，而且修法很小（在打包前多讀一次設定檔）。⚠️ **修的位置要對**——放在上傳那條路會漏掉網址那條；而且**要同時擋掉兩種讀法**（當成程式樣板跑，以及直接當程式碼執行）。
2. **第 2 條緊接著——它現在是唯一的防線。** 因為第 6 條已裁定**不限制可以連到哪裡**（目標是客戶自己的主機，該由客戶自己管），所以「誰可以按這顆按鈕」變成唯一擋得住的地方。**真正要補的只有「測試連線」這一支**（同樣沒掛的「查引用次數」是純查詢、不解密、不對外連線，不必動）。
3. **第 6 條同一個入口一起做，但方向改了**：不做目標白名單，改成限制單次台數、回應只給成功／失敗、日誌記到「誰按的、連到哪一台、用了哪一組設定」。
4. **第 3、4、5 條建議一起修**——都在「規則包怎麼進來、怎麼被檢查」這一段，而且第 3 條與第 4 條是**同一支驗證器的兩種病**（一個是「上限太晚生效」，一個是「上限根本沒接進這條路」）。修的時候把檢查放進解析那一層，未來的第三種來源天然涵蓋。
5. **第 7 條要跨出這一塊看**——同樣寫反的隔離規則在出貨基線裡共有 **11 條**，橫跨四塊功能，**只改本塊的五張等於沒修**。已裁定集中一次改完、不為此單獨重產基線，🔴 **但出貨前必須完成**；施工時**逐條比對、不要批次取代**（會把基線裡上百條正確的一起改壞）。⚠️ 動出貨基線屬決策者裁示範圍。
   🔴 **而且這 11 條要分兩組改**：**8 張**改成正確方向，**授權那 3 張只擋「改」和「刪」、「看」保持原樣**——授權的設計就是「照綁在最頂層、底下子單位共用」，把那三張改成跟其他表一樣會讓每個子單位查不到授權、**功能當場掛掉**。詳見[授權管理那塊](M18-license.html)。
6. **第 10 條的修改範圍比表面上大**——除了加上限，還要一併處理「手動重抽會主動把狀態壓回等待中」這個既有動作，否則新加的判斷擋不到任何東西。
7. **第 14 條不是這塊的新問題，但修的時候不能漏掉這塊**——那條原本記在「稽核流程」那塊，決策方向已經裁定，但原本記的範圍只有流程那塊的兩支功能；這次查出來，這塊還有八支吃同一道守門。**修那條的時候要把範圍一起擴大到這八支。**
8. **第 8 條順位最後。** 重新定性後已**調降為低風險**——那個功能本來就只開給租戶管理員，不是「任何登入者都拿得到」。真正的問題是**權限矩陣騙了管理員**（取消勾選只讓前端選單消失，後端照回）。補的時候**保留那顆權限點**、**一併改掉那句「讀取端刻意不掛」的說明文字**，並**驗證公版與分享的可見範圍沒有被改壞**。這個形狀與設備清冊那塊相同，建議兩處一次裁。

**最後要說清楚兩件事**：

- **「查完」不等於「查乾淨」。** 這 14 批查過的範圍裡沒再找到別的，但我們的方法能證明的是「找到了什麼」，不是「沒有別的」。
- **有 4 批的成本花在跟工具搏鬥、不是查程式**（一批 17 小時零產出、一批紀錄被誤刪、一批換四次人、一批派三位回一位）。這幾批涵蓋的檔案，可信度要打一點折；**而這也正是全站在 2026-09-20 決定止損、改成「針對已經發現的修」的依據**（見[總報告首頁](index.html)）。
:::

| 統計（2026-09-23 收口） | |
|---|---|
| 檢視批數 | **15 批**（模組本體 14 批＋主系統接線 1 批，已全部完成；其中 4 批的成本花在跟工具搏鬥） |
| 檢視的檔案 | **83 個**（正式程式碼 160 個，扣掉另一條線已查過的 11 個、與 66 個沒有邏輯的檔案） |
| **這塊找到的問題** | **13 條** |
| ↳ 高風險 | 2 條 |
| ↳ 中風險 | 10 條 |
| ↳ 低風險 | 1 條（第 8 條，重新定性後由中調降） |
| 另有別處問題波及到這塊 | 1 條（表中第 14 條，**不計入上面的 13 條**） |
| 已修 | 0 條 |
| 未修 | 13 條（＋波及的那 1 條） |
| 另記的體質問題 | 7 條——**跟著這塊修正一起做 1 條**、**只改說明文字不動程式 3 條**、**另開獨立工單 2 條**、**只記錄不處理 1 條** |
| 已開工單 | 0 張（決策者裁定先登記，修正工單另批統一開） |
| 主系統接線 | **已補查**（2026-09-23，12 個檔、一批）——範圍內沒有新的資安問題 |

---

| 你想知道 | 看哪裡 |
|---|---|
| 我們用什麼方法查、結果有多可信 | [檢視方法與工具](GUIDE-01-method-and-tools.html) |
| 為什麼有些批次要拆得很小 | [檢視方法與工具 → 為什麼要分批進行](GUIDE-01-method-and-tools.html#batching) |
| 其他模組的檢視結果 | [回總報告首頁](index.html) |
