Guidant AI 資安檢視 · 模組報告
產品裝到客戶機器上之後,負責確認「程式沒有被動過手腳」的那套機制。這塊有一條是我們實際動手測出來的——換掉其中一個零件,整套防護永久失效:開機核對、定期抽查、敏感操作前的即時驗證三層共用這個零件,會一起失效,而且照樣顯示通過。
我們的產品是裝在客戶自己的機器上的。那台機器歸客戶管,機器上有管理權限的人,理論上可以直接去改我們的程式檔案——改掉授權檢查、改掉稽核紀錄的寫入、改掉計分邏輯。
這塊就是防這件事的機制:
這塊的風險形狀和其他模組完全不同。
別的模組出事,是「誰看到了不該看的資料」。這塊出事,是「產品被改了但沒有人知道」——包含稽核紀錄被竄改、授權鎖被拿掉。
而稽核產品的價值,建立在「紀錄沒被動過」這件事上。
適用範圍:這六條全部只影響裝在客戶機器上的落地版。
程式裡寫死了:只有打包出貨的產物才會強制啟用這套保護,用原始碼直接跑的模式整套跳過。也就是說,我們自己的雲端版與開發環境都不啟用這塊。
這不是疏漏,是設計——這塊的假想敵,就是「機器不在我們手上」。
檢視期間:2026-09-10(一輪掃描 + 同日一次實際測試)|範圍:21 個檔案、約 3,250 行(另有我們主系統這端的 390 行接線,由統籌者全文讀完後寫進工作單,不重複查)|共 1 輪 + 1 次實測
原始技術報告放在需求中心的 FR-084 站,檔名見下表最後一欄。那裡有每一輪的完整技術細節、檔案清單與逐條推理,供需要深究或稽核抽查時查閱。
| 輪次 | 查什麼 | 檔數 | 覆核投票 | 結果 | 原始報告檔名 |
|---|---|---|---|---|---|
| T1 | 這套保護能不能被繞過、被關掉、或被拿來搗亂(鎖了解不開) | 21 | 6 票全投完 | 自動檢視零發現;統籌者親自查出 6 條 | scan-T1-package-core |
| T1b | 實際動手驗證其中最關鍵的那一條 | — | (實測,不投票) | ✅ 確認成立,維持高風險不降級 | scan-T1b-verifier-swap-poc |
這一輪自動檢視的正式結果是「零發現」,但這塊實際有 6 條問題
自動檢視提了 2 個項目,覆核投票後都沒過門檻(一項 1:2、一項 0:3),所以工具的正式報告是零。
6 條全部是統籌者親自開檔查出來的。 這是連續第四個模組出現同樣情形——工具不會照著工作單上列的重點去查。
這塊是整份報告中少數有實際測試佐證的
第 1 條原本只是讀程式碼得出的推論。2026-09-10 我們在自己的電腦上真的動手做了一次:把負責核對簽章的那個零件換成一個「永遠說沒問題」的假貨,看看被改過的檔案會不會就這樣通過。
會。 開機檢查與四小時一次的抽查兩個都通過、全程沒有任何警示;第三層(敏感操作前的即時驗證)用的是同一個被換掉的零件,因此一併失效。
全程只在暫存目錄操作,沒有連過任何正式環境,也沒有改過任何一行產品程式碼。
排序按風險,同風險的同一類排在一起。「出事會怎樣」那欄講的是業務影響——誰受影響、損失什麼,不是技術現象。
這塊有六條問題共同的前提是:攻擊者已經取得那台機器的管理權限。所以要問的不是「需不需要主機權限」,而是**「有了主機權限之後,關掉這套保護還要不要額外成本」**。
| # | 問題 | 風險 | 分類 | 出事會怎樣(誰受影響) | 怎麼修 | 狀態 |
|---|---|---|---|---|---|---|
| 1 | 負責核對簽章的那個零件,被歸在開機時不會檢查的那一層——換掉它,整套防竄改永久失效 | 🟠 高 | 防竄改機制被削弱 | 客戶那台機器上的產品可以被任意改動,而系統照樣顯示一切正常——授權鎖可以拿掉、稽核紀錄可以竄改、計分邏輯可以改,全程不會有任何警示。三層檢查(開機核對、四小時抽查、敏感操作即時驗證)會一起失效,因為三層用的都是同一個被換掉的零件 | 打包時把這個零件強制編譯進核心層,讓它不再是一個可以被單獨換掉的檔案 | ✅ 已修(CM-2053,待 188 全量 build 實證) |
| 2 | 文件說有「檔案標記」與「資料庫紀錄」兩道鎖,程式其實只做了一道——刪掉一個檔案,被鎖的機器就能重開 | 🟠 高 | 防竄改機制被削弱 | 客戶端發生竄改事件後,只要一行指令就能把「被鎖定」這件事抹掉、讓機器重新開起來。而那筆鎖定紀錄正是這套機制原本要保留的證據。最糟的是維護的人以為有兩道鎖,因此不會想到要去補這個缺口 | 兩件事都要做:止血是把文件改成跟程式一致(零成本)、治本是開機時另外去查一次資料庫紀錄 | ✅ 已修(CM-2053)。開機查資料庫時若「連得上但沒有權限讀」改成拒絕開機(FR-114 CM-2112);刪除竄改紀錄表仍會放行,屬已知限制——同時刪標記檔與刪表需主機與資料庫管理員權限,超出本機制「擋隨手改檔」的定位 |
| 3 | 有一個設定可以把「要核對哪個目錄」整個換掉,而且它的優先順序比「是不是正式打包版」還高 | 🟡 中 | 防竄改機制被削弱 | 單獨用它不成立(指到空目錄會因為找不到清單而拒絕開機)。但它是第 1 條的放大器——搭配起來,攻擊成本從「要偽造一整份清單」降到「隨便準備一個目錄」 | 在打包模式下忽略這個設定 | ✅ 已修(CM-2053) |
| 4 | 已經用過的解鎖檔,如果使用紀錄毀損,系統會當作「一張都還沒用過」 | 🟡 中 | 防竄改機制被削弱 | 把記錄檔改成亂碼,用過的解鎖檔就能重新復活使用。實際影響不大(還受機器指紋、事件編號、有效期限三道限制),但一次性通行碼原本該有的保障,被降低成「只要能寫入一個檔案就能破壞」 | 把「檔案不存在」(合理)與「檔案內容毀損」(不合理,應該拒絕)分開處理 | ⚠️ 部分修(CM-2053,解鎖碼帳本毀損目前只記高等級警示不拒絕,原廠有重簽流程後再收緊) |
| 5 | 鎖定畫面會把機器指紋與鎖定事件編號顯示給任何連得到這台機器的人看 | ⚪ 低 | 回應夾帶不該送的欄位 | 任何打得開這個系統網址的人都看得到這兩個值,包含還沒登入的人。**評低是因為這兩個值本來就是「報修用的識別碼」——拿著它們要換到解鎖檔,還得通過原廠的人工窗口確認身分 | 不修**(決策者裁定,理由見下方) | ✅ 不修 |
| 6 | 回報「偵測到竄改」時,會把主機紀錄檔最後 50 行原封不動送回原廠 | ⚪ 低 | 敏感內容寫進日誌 | 那 50 行若剛好夾帶密碼或登入憑證,就會一起被送出去。評低是因為這個回報目前送的是本機位址,不會離開那台機器 | 送出前套用系統其他地方送日誌時用的那套遮蔽規則——工具是現成的,不需要重寫 | ✅ 已修(CM-2053) |
| 7 | 防竄改記下的「旁證」裡,來源 IP 讀的是用戶端自己可以填的欄位 | ⚪ 低 | 外部送什麼就收什麼 | 出事時要追「是從哪台電腦來的」,記下來的 IP 可能是對方自己偽造的,旁證失真。不影響防竄改本身擋不擋得住 | 改讀真正的連線位址,不信任用戶端可以填的欄位 | ✅ 已修(FR-114 CM-2171,BE commit 908f9dd0e,1.21.0 出貨)。這條是主系統掃描查出的 |
下面只展開需要你自己判斷、或容易被誤解的幾條。 其餘的修法明確,看上面表格的「怎麼修」那一欄就夠了——沒有展開不代表沒查,也不代表不重要。
這是整份報告中少數「我們實際動手測過、而且確認成立」的問題。
原本只是讀程式碼得出的推論,2026-09-10 實測後證實:不但成立,而且失效得比推論的更徹底——開機檢查、定期抽查、敏感操作的即時驗證三層同時失效,全程零警示。嚴重度維持高風險,不降級。
讀者看到「出問題的零件是別人寫的公開套件」,第一反應很容易是「那是第三方的事,不歸我們管」。這個判斷是錯的。
這一條保護的對象是我們自己的核心程式。攻擊者去動那個第三方零件,目的不是拿第三方怎樣——是要讓「核對我方程式有沒有被改過」這件事失效,好接著隨意竄改我們的業務程式、授權鎖與稽核紀錄。
換句話說:第三方那支零件是工具,我們的產品才是目標。
這套防竄改機制的工作方式,是「拿一份原廠簽過名的檔案清單,逐一核對機器上的檔案有沒有被改過」。
問題在於:負責『核對簽章』的那個零件本身,不在被核對的範圍內。
像是鎖了大門,但鑰匙孔本身沒有人看守——把鑰匙孔換成一個「插什麼都開」的假貨,門就形同虛設。
攻擊手法是「掉包」,不是「刪掉」——這兩件事的後果完全相反
| 會發生什麼 | 對攻擊者有沒有用 | |
|---|---|---|
| 把那個零件砍掉 | 服務直接起不來,畫面會顯示系統已鎖定 | 沒用。動靜很大、而且什麼也拿不到 |
| 把那個零件換掉 | 服務照常啟動,而且印出「驗證通過」 | 這才是攻擊手法——安靜、全部放行、沒有任何警示 |
報告講的這一條,指的是後者。
出貨的程式分成三層:
| 分層 | 內容 | 開機時會檢查嗎 |
|---|---|---|
| 核心 | 我們自己寫的程式 | ✅ 會,全部逐檔核對 |
| 資源 | 隨版附帶的資料檔 | ✅ 會,全部逐檔核對 |
| 第三方 | 別人寫的公開套件 | ❌ 不會,只交給每四小時一次的隨機抽查 |
負責核對簽章的那個零件,剛好落在第三層——而且不是誰決定把它放過去的,是被「連坐」帶進去的:它原本不在「不編譯、原樣照抄」的名單上,但名單上另一個套件(負責解析 PDF 的那支)依賴它,打包流程會把被排除套件的所有相依一併照抄過去。
順帶澄清:第三方套件不是「沒有被打包進去」
第三方套件有進到出貨產物裡,只是原樣複製、沒有編譯,所以在客戶機器上是可讀、可改的檔案。
我們自己寫的業務程式,以及全部 21 支自家套件,一律編譯。 打包腳本裡有一道硬性檢查:產物裡的原始程式碼檔案必須為零,只要驗到一個就中止打包。所以不會有我們的明碼流出去。
| # | 問題 | 結論 | 根據 |
|---|---|---|---|
| ① | 那個零件真的落在「開機不核對」的第三層嗎? | 是 | 用打包腳本自己的邏輯重跑一次,被排除的 37 個相依套件裡確實包含它 |
| ② | 換掉之後,開機檢查會放行被改過的檔案嗎? | 會放行,而且印出「驗證通過」、正常繼續啟動 | 實測 |
| ③ | 四小時一次的抽查是不是也一起失效? | 是,一起失效,回報「通過」 | 實測 |
為什麼另外兩層也救不了:四小時一次的抽查、以及敏感操作前的即時驗證,用的都是同一個已經被換掉的零件。三道防線共用同一個支點,支點倒了就一起倒。
決策者拍板的方向:把它編譯進核心層,不是「多加一道檢查」。
這是消除縫隙,不是增加檢查——核心層是整包編譯後一起簽名的機器碼,那個零件融進去之後,機器上就不存在「那個檔案」可以被換掉,攻擊動作本身就不成立。
| 步驟 | 做什麼 | 為什麼 |
|---|---|---|
| 主修 | 打包時把這個零件從「不編譯、原樣附帶」的連坐名單裡拉回來,強制編譯進核心層 | 見上方:讓「被單獨抽換」這件事在物理上不可能發生 |
| 配套 | 把抽查邏輯改成「保證涵蓋安全關鍵檔案 + 其餘隨機抽」 | 擋不住本條(理由見下),但對第三方層其他檔案仍然有用——目前是純隨機,關鍵檔案可能很久才被抽到一次 |
| 前置 | 在打包機上實際編一次、跑一次落地版確認功能沒壞,才能定案 | 這類加密零件常帶原生元件,我們過去踩過「編譯進去反而載不起來」的坑。這個驗證只碰打包機,不碰任何客戶環境 |
一個被排除的修法,以及為什麼它無效
原本報告把「把這個零件列入開機必查」列為首選修法。經過實測情境檢視後,這條擋不住已經驗證過的攻擊手法,因此從建議中移除。
理由:檢查流程是「先用那個零件驗清單的簽名,簽名對了才照清單逐檔比對」。零件被換掉之後,任何簽名都會被判定通過——攻擊者可以把清單一起偽造,把假貨自己的指紋填進清單裡。變成自己驗自己。
2026-09-10 的實測做的正是「換零件 + 自製清單」這兩步,所以這個修法在該情境下沒有作用。
文件寫的是:偵測到竄改之後會在兩個地方留下鎖定紀錄——檔案系統一份、資料庫一份,只要任一份還在,機器就拒絕開機。
實際上程式只做了檔案那一道。 查資料庫那一半的程式寫好了,但沒有任何一個地方呼叫它。程式裡甚至自己留了一段說明,承認補同步機制的目的就是「把檔案標記被清掉這個漏洞的風險降到最低」。
結果是:只要有主機的操作權限,刪掉那一個檔案,被鎖住的機器就重新開起來了。
覆核以 1:2 判定不成立,但決策者裁定維持高風險。
因此維持高風險。
補強與已知限制:開機查資料庫時若「連得上但沒有權限讀」改成拒絕開機(FR-114 CM-2112);刪除竄改紀錄表仍會放行,屬已知限制——同時刪標記檔與刪表需主機與資料庫管理員權限,超出本機制「擋隨手改檔」的定位。資料庫連不上、表不存在、其他未預期錯誤仍維持放行並印警示——開機時資料庫晚起是常態,不能因此擋住正常客戶。
決策者裁定:這不是「選一個」,是「兩件事都要做」。
改文件是止血、零成本、而且擋得住「維護者以為已經有兩道鎖、因此不會想到要補」這個更麻煩的狀況;補開機查資料庫是治本。兩者不衝突,可以分開排。
| 做什麼 | 成本與注意事項 | |
|---|---|---|
| **止血(先做) | 把文件改成跟程式一致——目前只有一道鎖 | 成本零,可以現在就做。** 目前最糟的狀況是維護者以為有兩道鎖,因此不會想到要補這個缺口 |
| **治本(另排) | 開機時另外開一條簡短的資料庫連線去查那張紀錄表 | 那張表刻意設計成不受客戶資料隔離限制、不需要登入身分,技術上可行。施工難點**:開機這段刻意只依賴最基本的東西(沒有資料庫連線、沒有框架),要加查詢得另外開一條乾淨的連線,不是改一行的事 |
| 治本的替代做法 | 讓補同步機制在發現「資料庫有紀錄、但檔案標記不見了」時,反過來把標記補回去並終止開機 | 目前的同步只有單向(檔案有變動才更新資料庫),反過來不會。可以取代上一列 |
治本要做對的話,判斷邏輯必須是「任一邊有案底就拒絕開機」
不是「兩邊都認定有問題才算數」。兩個落點的用意是互為備份——攻擊者要同時抹掉兩邊才能脫身。寫成「都要認定」等於把兩道鎖退回成一道。
這條的結論是「不修」,但原本寫的評低理由是錯的,必須換掉。
原本寫的是:「評低是因為正式部署時只讓內部網路看得到這個服務,實際打不到。」
這句不成立。 鎖定畫面的那個服務,佔用的就是後端原本對外提供服務的那個出入口——客戶本來就要從外面連得進來才用得到系統。程式裡的說明也寫明它必須沿用原本的出入口、必須回應任何來源,否則前端收不到「已鎖定」的訊息、鎖定畫面根本不會出現。
所以任何打得開這個系統網址的人都看得到那兩個值,包含還沒登入的人——機器都被鎖住了,連登入都沒有。
為什麼一定要改:留一句稽核方查得出來是錯的理由,反而是給人挑毛病的把柄。
換成正確的理由,結論仍然是「不修」:
| 那兩個值是什麼 | 機器指紋與鎖定事件編號——本質上就是報修時要報給原廠的識別碼,設計上就是要給人看的 |
| 拿到它們能做什麼 | 要換到解鎖檔,必須通過原廠的人工窗口。解鎖是極少數事件,會來找我們的必然是既有的客戶窗口,我們自己人工就知道對方是誰 |
| 攻擊者缺的是什麼 | 機器指紋與事件編號他拿得到,客戶窗口的身分他拿不到 |
風險等級維持「低」,換掉的是理由不是等級。
連帶移除兩件事
修法只有一個:套用遮蔽規則。原本並列的「只送行數與時間」已刪除。
為什麼不能只送行數與時間:那 50 行是刻意設計的證據來源。竄改多半發生在服務停止期間,而這份紀錄記的是上次運行的最後狀況——把它和檔案被修改的時間交叉比對,可以框出竄改發生的時間區間。只送行數等於把這份鑑識價值整個丟掉。
為什麼遮蔽是可行的:系統已經有一套現成的遮蔽規則,其他地方送日誌出去時就在用,涵蓋密碼、金鑰、通行證這類欄位。直接套用即可,不需要重寫。
修法寫法:送出前套用與系統其他地方送日誌時相同的遮蔽規則——保留鑑識價值,拿掉外洩風險。
一句話:這套防竄改擋得住隨手改個檔案,擋不住已經拿到主機權限、而且知道它怎麼運作的人——其中兩條,一個動作就能得手。
| 形狀 | 哪幾條 | 說明 | |
|---|---|---|---|
| ① | 支點沒人守 | 第 1 條 | 驗簽名的零件自己不被保護。三層檢查全部靠它,它倒了就全倒。這是這塊真正的問題。 |
| ② | 想到了,只做了一半 | 第 2、4 條 | 查資料庫的程式寫好了沒人呼叫;「帳本壞掉」與「帳本不存在」被寫成同一件事 |
| ③ | 放大器與周邊 | 第 3、5、6 條 | 第 3 條讓第 1 條更省事;第 5、6 條是資訊外洩——第 5 條的兩個值看得到也用不了(換解鎖檔要過原廠人工窗口),第 6 條目前沒離開那台機器 |
| 順序 | 做什麼 | 成本 |
|---|---|---|
| 1 | 第 1 + 3 條合併一張工單——把驗簽名的零件編譯進核心層,同時讓正式版忽略那個目錄設定 | 中。前置是在打包機上編一次、驗證功能不會壞 |
| 2 | 第 2 條的止血——把文件改成跟程式一致 | 零,可以現在就做 |
| 3 | 第 6 條——回報前套用現成的遮蔽規則 | 小 |
| 4 | 第 4 條——帳本讀不出來就拒絕核銷,照同一套件隔壁那支的寫法 | 小 |
| 5 | 第 2 條的治本——開機補查資料庫 | 中(開機段刻意不依賴資料庫,要另開一條乾淨連線) |
| 6 | 第 5 條不修(決策者已裁定,評低的理由也已在本報告更正) | — |
第 1 與第 2 項建議一起排:第 1 項是唯一有實測佐證的,第 2 項零成本、而且能防止維護者繼續誤解。
這塊的可信度要老實講——不要以為查得跟其他模組一樣徹底
| 統計 | |
|---|---|
| 檢視輪數 | 1 輪、21 個檔案(另加 1 次實際測試) |
| 找到的問題 | 6 條 |
| 高風險 | 2 條 |
| 有實測佐證 | 1 條 |
| 已修 | 4 條(第 1/2/3/6 條,CM-2053) |
| 部分修 | 1 條(第 4 條,CM-2053) |
| 裁定不修 | 1 條(第 5 條) |
| 未修 | 0 條 |
| 已開工單 | 0 張(依決策者裁定,全部掃完後統一開) |
還沒查的三件事(原始報告自己列出來的):