Guidant AI 資安檢視總報告 · 第一章

我們用什麼方法檢視,結果有多可信

這一章回答一個問題:這份報告憑什麼可以相信? 我們怎麼查的、用了什麼、哪些地方查得徹底、哪些地方我們老實說「還沒查」。

§1

做法摘要

我們請 AI 把整套系統的程式碼一行一行讀過去,像找碴一樣挑出可疑的地方;每挑出一個疑點,再另外找三個獨立的 AI 重查一遍投票表決;最後由人親自把每一條打開來確認。

為什麼要這麼麻煩? 因為資安問題分兩種:

一種是「寫法危險」

用了過時的零件、密碼直接寫在檔案裡。 這種有現成工具可以自動掃,快又便宜,我們 7 月做過一輪。

一種是「少了一道判斷」

程式寫得完全正常,只是忘了問一句「這筆資料是你的嗎」。 自動工具永遠掃不出來——因為程式沒有寫錯,是漏寫。

這次要找的是第二種。代價是慢、貴,而且不能保證每個角落都看過——這一點我們在最後一節老實交代。


§2

一次檢視是怎麼進行的

為什麼要拆這麼多道手續

讓 AI 單獨去找漏洞,會犯兩種錯:看走眼說有(其實沒事),和讀漏了說沒有(其實有洞)。

前者靠「三個人獨立重查投票」擋下,後者靠「人親自逐條核對」補上。兩道都做,結論才站得住。

第 1 步:先自己看過,圈出最可疑的地方

負責主導的人(下面稱統籌者,也是最後為結論負責的那個人)會先自己把這批程式讀一遍,圈出「最可能出事的五到八個地方」,寫成一份工作單。

這一步是刻意的——如果不先圈,AI 會漫無目的地找;圈了之後,等於先告訴它「這幾個地方我特別懷疑,你去驗給我看」。

第 2 步:交給一個人負責執行

這個人開一個獨立的環境準備動工。他不負責按下開始鍵——他只先核對「要查的東西數量對不對」,對完就停下來回報。

第 3 步:由人親手下令開始

這個工具刻意設計成「AI 自己叫不動」

必須由人本人當場打字下令,而且要打一句「我知道這會跑很久、會花不少錢」才會開始跑。

工具的作者不希望 AI 在人沒注意的時候,按下一個要跑好幾小時、花掉大筆費用的按鈕。

副作用是:這件事沒辦法排在半夜自動跑,一定要有人在。

第 4 步:找出疑點

AI 把這批程式從頭讀到尾,挑出它覺得可疑的地方。

它的標準偏嚴——要能講得出「壞人具體要怎麼做才能得手」才會提出來。所以它比較容易漏掉,不太會亂報;報出來的通常都是真的。

第 5 步:每個疑點,再找三個人重查一遍

找出疑點的那一個,不能自己說了算。

每一個疑點,都會另外找三位重新查證——他們彼此不認識、也看不到前一位的推理過程,各自從頭把相關的程式看過一遍,再各自說「我認為這是真的問題」或「我認為不是」。

三票結果 怎麼處理
三票都說是 這個問題成立
兩票說是、一票說不是 成立,但反對的理由會記下來——通常會影響我們判斷它有多嚴重
三票都說不是 當作誤判,不列入

這一關是有牙齒的,不是走過場

有一次四個疑點裡被否決了兩個,否決的理由具體到打開程式指出:「這裡最多只能填六個欄位,多填會直接報錯,根本塞不進東西。」

第 6 步:人親自核對每一條

AI 全部跑完之後,統籌者還要自己做三件事:

① 每一條嚴重的,自己打開來看

不是看 AI 的結論,是自己把那段程式叫出來、順著追到最外面的入口,確認真的沒有人在把關。最嚴重的那幾條,要把「壞人從第一步到得手」整條路走一遍。

② 補查 AI 沒回答的問題

第 1 步圈出的那五到八個懷疑,AI 經常一個都沒回答。這些要人自己回頭查,而且報告上要註明「這是人查的,不是 AI 找到的」。

③ 把「不屬於這一批」的挑掉

AI 為了搞懂前因後果,常常會跑去讀不在這次範圍內的程式,然後把別人家的問題當成這次的發現報上來。有一次九條發現裡,八條其實是別批已經報過的。

重查的那三位只管「這個問題是不是真的」,不管「這是不是這次該查的」——所以要人拿範圍清單一條一條比對挑掉。

一次檢視通常要一到兩個小時,最久的一次跑了五個半小時。


§3

為什麼要分批進行

我們不是「把整個系統丟進去查一次」,而是切成很多小批。這不是謹慎,是不分批根本跑不完。

分批的上限是怎麼訂出來的

一次貪心,全軍覆沒

曾經有一次,我們一口氣放了 42 個檔案進去。結果負責重查的 105 個 AI 全部超過用量上限被中斷,21 張票一張都沒投出來——那一次等於白跑。

後來又有一次,連續試了五次全部無功而返,才把上限再往下收。

現在的規矩是兩條,哪條先到就停:一批不超過 15 個檔案,而且不超過兩千行程式。

真正花錢的是「重查」,不是「找」

一般人會以為「讀程式找漏洞」最花資源。其實不是——最花的是重查投票:找出幾個疑點,就要另外找幾組三個人,每一位都得從頭把程式再讀一遍。

而疑點的數量大致跟範圍成正比。所以範圍放大一倍,成本不只放大一倍。

怎麼切也有講究

照功能切,不照技術層次切

每一批拿一個完整功能,從「使用者按下按鈕」一路追到「資料存進資料庫」。

因為權限漏洞最常躲在銜接處——只給上半截或只給下半截,兩邊都看不出問題,合起來才看得到。

真的切不開的時候,寧可讓相鄰兩批重疊、把銜接的部分都帶進去,重複報的再由人挑掉。銜接處沒人看,才是真的漏掉。

範圍跨兩個程式庫時,分兩次掃

有些功能的程式一半在外部模組、一半在我們主系統。工具一次只能看一個程式庫——範圍跨兩邊時,另一邊的檔案會被安靜地丟掉,不報錯、不提醒,看起來就像「那些檔案查完沒事」。

所以這種範圍一律配對切成兩次掃:模組那邊一次、主系統那邊一次,再由執行者手動核對兩邊接起來的地方(主系統傳了什麼給模組、模組回了什麼)。稽核輪次管理那塊九輪全部這樣做,其中一條問題(稽核計畫 Word 的壓縮炸彈)兩邊各自獨立掃到,正好互相印證。

「兩千行」只是粗略的尺

真正的判準是「要讀進去多少東西」。有一個兩千五百行的單一檔案順利跑完了,反而有一批一千五百行、但分散在五個檔案的連續失敗六次——因為要在五個檔案之間來回翻。

中文註解特別多的部分,同樣行數要消化的量是英文的兩三倍。


§4

怎麼判斷一次檢視「有沒有做完」

每跑完一次,工具會自動留下一張紀錄,寫著這次找出幾個疑點、投了幾票、以及有沒有完整跑完。

這張紀錄是我們判斷「這次結果能不能採信」的依據。

「沒跑完」有三種,意思完全不同——不能一律當失敗

哪一種 實際狀況 能不能採信
① 重查完全沒跑起來 一票都沒投出來 不能說「這裡查過了」。可以靠人工核對開工單修,但不能宣稱檢視過
② 中途超過用量、恢復後接著跑 工具記下中斷的,額度恢復後用完整三人重投 這其實不算失敗,票數是完整的,照常採用
③ 跑完了但存檔出錯 投票完整跑完,只是最後存檔時有幾條的路徑寫錯被退掉 是存檔問題不是查證問題,以人工報告為準,被退的補回去即可

所以看到「沒跑完」要先看是哪一種,不能直接當成白跑。

還有一個陷阱:一個疑點都沒找到的時候,紀錄上也會顯示「完整跑完」——那是「沒東西可投票」,不是「這裡很乾淨」。這種情況我們會另外標出來。


§5

一個問題要過幾關才算數

%%{init: {"theme":"base","themeVariables":{"background":"#ffffff","primaryColor":"#eef2ff","primaryTextColor":"#1e293b","primaryBorderColor":"#6366f1","lineColor":"#64748b","secondaryColor":"#f1f5f9","tertiaryColor":"#f8fafc","fontFamily":"ui-sans-serif, system-ui, sans-serif","fontSize":"14px"}}}%%
flowchart LR
  A["提出疑點<br/>(要說得出壞人怎麼得手)"] --> B{"三個人<br/>獨立重查投票"}
  B -->|"三票否決"| D["不列入<br/>但人仍會看一眼"]
  B -->|"通過"| C["人親自核對<br/>打開來看/挑掉不屬於這批的/重新評估嚴重度"]
  C --> E["列入問題總表<br/>一句白話結論"]
圖 1 — 一個疑點從提出到列入報告,要過四關

被否決的也要看一眼。 重查的人判斷「現在打不到」通常是對的,但他只回答了「現在打不打得到」,沒有回答「這段程式本身是不是寫錯了」。

有一次,一個被三票一致否決的疑點,統籌者核對後發現:他們說「打不到」完全正確,但漏看了同一段程式裡兩行用了不同的東西在比對——那是另一個問題,只是不會被壞人利用。

也有反過來往上調的:有一個問題,重查的人只敢說「中等把握」,因為卡在一件要查資料庫才知道的事。統籌者真的去查了,確認成立,把把握度提到「高」。


§6

我們用了什麼

這一次(2026 年 9 月)

用的是 Claude Security——Claude Code 官方推出的資安檢視外掛,由 Anthropic 開發。

白話說,它做的事是:派出幾十個獨立的 AI 去讀程式碼,一部分負責找問題,一部分負責重查投票,跑完產出一份報告和一張「有沒有做完」的紀錄。

官方原始碼與說明 anthropics/claude-plugins-official → plugins/claude-security
產品介紹頁 claude.com/product/claude-security
目前狀態 公開測試版(beta),所有 Claude Code 使用者都能裝
我們用的版本 0.10.2.3(官方已出 0.11.0,我們刻意不升——比對過差異只動報告格式,對眼前的問題沒幫助)

它是 Anthropic 另一項雲端服務的**「在自己電腦上跑」的版本**:同樣的檢視能力,但整個過程都在我們自己的環境裡完成,程式碼不會傳出去。

官方自己講明的三件限制(我們照單全收,也寫進這份報告)

  1. 結果不是每次都一樣——「同一份程式碼掃兩次,可能找到不同的問題」。所以官方的建議是定期掃、靠累積把涵蓋面補起來,不是掃一次就當作查完。這正是我們這份報告最後一節要老實交代「哪些不能說查過了」的原因。
  2. 它是補強,不是取代——官方原話是它「用資安研究員的方式去理解程式碼」,補的是自動掃描工具看不見的那一塊,不是拿來取代原有的自動掃描與人工審查。
  3. 它沒有額外的隔離保護——它用的是執行者本人的權限,所以只適合用在自己掌握的程式碼上。我們掃的全是自家產品,符合這個前提。

另外兩個設計,直接支撐了這份報告的可信度:

  • 重查的人是被要求「去推翻它」的,不是去附和。官方的說法是:除非能確認真的有一條可以被利用的路徑,否則就判定為誤報。沒過關的連提都不會提——「這就是為什麼報告都很短」。
  • 「查得夠不夠徹底」那個紀錄,是程式算出來的,不是 AI 自己說的。也就是說,AI 不能自己宣稱「我查得很徹底」。

上一次(2026 年 7 月)

那一次用的是三個性質完全不同的工具:

工具 它是什麼 負責什麼
Semgrep 一本「危險寫法字典」,拿去比對程式碼,命中就報 程式裡的危險寫法。後端一次比對了 1,144 條規則
Trivy 一張「成分表對照器」——把我們用到的現成零件版本,去對全世界公開的已知漏洞清單 零件有沒有已知漏洞、密碼有沒有寫死在檔案裡
SonarQube 一個長期駐點的「健康檢查站」,常態盯著整個系統的品質 問題數量的長期追蹤

那一次的成果:後端高風險的零件漏洞從 15 個降到 0,前端從 83 降到 18。

兩次的差別(這點最重要)

查「寫錯了嗎」上一次:拿字典比對

查「漏了嗎」這一次:讀懂了再判斷

上一次 這一次
找得到什麼 零件過舊、用了危險的寫法、密碼寫在檔案裡 忘了檢查資料是不是你的、權限判斷寫反了、要繞三個功能才能拼出來的攻擊路線
快慢與成本 快、便宜,每次改程式都能跑 慢、貴,一次要一兩個小時
定位 日常把關 定期深度檢視,不是全面保證

為什麼非得做這一次不可

因為自動工具永遠找不到這次找到的東西——程式寫法完全正常,錯在漏了一道判斷。

最乾淨的例子:問卷功能裡,六個「寫入」的入口全部有檢查權限,八個「讀取」的入口一個都沒有。同一個檔案裡、寫法一模一樣,差別只在有沒有多問一句「這是你的嗎」。

字典比對看不出這種事——因為沒有任何一條規則會說「這裡少寫了一行」。


§7

這套方法的極限

這一節是刻意寫的。 知道它哪裡不可靠,才知道哪些結論要打折、哪些地方一定要人補上。

四個固定的盲點

# 盲點 後果
1 會跑出範圍 為了搞懂前因後果去讀別人家的程式,然後把別人的問題報成這次的發現
2 會被註解說服 有一次八個檔案查完零發現,但其中一支真的有洞——因為程式旁邊的註解寫著「這是故意這樣設計的」,AI 讀到就信了。沒找到不等於乾淨
3 投票可能整個沒跑成,而工具會記成「零發現」 看起來像「很乾淨」,其實是「根本沒查」
4 報出來的都是真的,沒報的不保證沒有 它標準嚴,寧可漏掉也不亂報

兩個要知道的行為

  • 指定「只看這幾行」它做不到——每次都會把整個檔案讀完。同一個檔案分兩半查,第二次的結果幾乎跟第一次重疊。
  • 它不一定會交代「每個檔案都讀完了」。有一次 33 個檔案查完,沒有留下「每一支都看到結論」的紀錄。

最大的一個坑:工具本身的軟體錯誤

AI 被自己的「超時保護」誤殺

看到的現象:負責找問題的 AI 讀著讀著就被中斷、什麼都沒產出,一再重來一再失敗。

真正的原因(2026-09-20 查明):AI 讀太多東西的時候,系統會自動幫它「整理記憶」,整理要花兩三分鐘、期間完全沒有動靜;而工具的保護機制規定「三分鐘沒動靜就當它卡死、砍掉重來」。

所以死掉的不是 AI 想太久,是被自己的保護機制誤判成當機。

這是工具官方已知的錯誤,別人比我們更早踩到並回報:

Workflow stall watchdog kills agents during auto-compaction(anthropics/claude-code 問題編號 92424,2026-09-06 提出)

回報的人拿出了很完整的證據:他分析了 751 份執行紀錄,其中 33 次是這樣死的——每一次都是「最後一筆動作之後 180 秒整被中斷,中間完全沒有輸出」。他也量出整理記憶實際要花多久:一半的情況 156 秒,最慢 205 秒——而保護機制的容忍上限正好是 180 秒。

截至 2026-09-20,這個問題仍然是開啟狀態:沒有人認領、沒有官方回覆、也沒有任何修復。而且沒有任何設定可以避開——回報者試過調整觸發時機,但「180 秒」跟「重試六次」這兩個數字都不給改。

代價有多大:其中一次,同一個範圍打了 17 小時、重試 25 次、一個結果都沒有才喊停。回頭看那 25 次紀錄,有 22 次只寫了開頭一句話就被砍掉。

我們怎麼因應:每批再壓小、優先挑單一大檔而不是分散在多個檔案、跑大批的時候不同時跑別的、看失敗紀錄而不是看畫面,第二次失敗就停手,不讓它一直重試。


§8

為什麼不能只看 AI 的結果

兩個數字說明問題:

3 次AI 報「沒發現問題」,實際都有真問題

約一半一開始的懷疑,後來證實是誤會

第一個數字:有三次 AI 正式回報「零問題」,但那三次其實都有真的問題,全部是人自己查出來的,包括證據最扎實的那一條(直接去資料庫撈出來證明)。

「工具沒查到問題」絕對不能當成「這裡是乾淨的」。

第二個數字:一開始列的懷疑,大約有一半後來被推翻。這是健康的。

被推翻的原因分別是:那段程式根本沒人在用、邏輯走不到那個分支、執行順序剛好擋住了攻擊路線。

為什麼「查清楚是什麼擋住了」跟「找到一個漏洞」一樣有價值

因為擋住它的如果是巧合,那哪天有人整理程式碼把那個巧合改掉,漏洞就自己回來了——而且沒有人會發現。

所以報告裡不只寫「這條不成立」,還要寫「是什麼東西擋住了它」。

兩個具體例子:

  • 有一次,工作單上自己標明「這批最重要」的那個懷疑,AI 完全沒碰;統籌者自己補查,證實成立,而且是那一次最嚴重的一條。
  • 有一次八個檔案零發現,但其中一支真的有洞,只因為旁邊的註解寫著「這是故意的」。

工具零發現的那幾輪,靠實測補上

有一類問題工具特別不擅長:「一份刻意做壞的檔案,會不會讓系統卡住或記憶體爆掉」。工具讀程式碼只能估「這裡大概會慢」,估不出「慢到什麼程度、要多大的檔」。

所以合規文件核心與稽核輪次那幾輪,工具一條都沒提的時候,執行者自己動手做惡意檔案實際去打,量時間與記憶體:

哪一輪 做了什麼 量到什麼
CMMC 官方 PDF 解析器 手做 7 種惡意 PDF(上千到兩萬頁、壓縮炸彈、單行幾萬字),另拿正常 PDF 亂數變造 3,000 次看錯誤訊息 兩萬頁吃掉 3.4GB;五頁、每行四萬字跑 52 秒;連修法都實測對照(每頁讀完就關,記憶體 534→128MB)
主系統的 Word 內容抽取器與格式轉換器 範圍內每一條比對規則拿攻擊字串逐條實跑,另手做惡意 Word 3,200 個空白 66 秒、三千個空段落 96 秒、一億欄的表格跑六分鐘未停
稽核計畫 Word、稽核結果 Excel 匯入 手做壓縮炸彈與「最後一列很遠」的 Excel 4.9KB 的檔在第 20 萬列填一格,10.9 秒、355MB

這幾輪「找到的存在嗎」可信度反而比投票還高——因為是量出來的數字,不是推論。但也要老實講打折處:多數沒有用客戶真實檔案跑完整流程,少數數字是從實測結果往上推算的(報告裡都標了)。

「有人守了一半」:八種長相

這一輪最常見的問題形狀,是有人把一條路守得很仔細、另一條路整個沒守——而守得仔細的那一半,正好證明開發者知道該守。工具很難看出這種問題,因為每一行程式都沒寫錯,是「本來應該有但沒有」。我們一共看到八種長相:

長相 白話說明
① 同一支程式隔幾行,一守一不守 改「分享設定」那條路有檢查歸屬,改「內容」那條路沒有
② 兄弟入口,一個掛一個沒掛 做同一件事的兩個功能,一個有權限檢查、一個沒有
③ 同一支檔案,寫入都守、讀取漏掉 新增、修改、刪除都要權限,唯獨「看」不用
④ 從別處複製過來,只補了寫那一半 程式從另一處複製改寫,補權限時只補了寫入,讀取整排沒補
⑤ 好幾個入口,只查過其中一個 三個匯出入口,當初只確認過一個
⑥ 好幾支方法,只有一支真的漏 六支都看起來一樣,其中一支少了那一行
⑦ 同一件事兩個入口、兩套門 畫面上一條條改要「修改」權限,走匯入整批覆蓋只要「建立」權限
⑧ 檢查甲、動手改乙 檢查的是網址上那個編號,真正動手改的是使用者另外送上來的編號

所以驗收補權限,不能數「有幾支掛了守門」

曾經有一塊被判定「守門看起來完整、不必單獨查」——理由是 25 個入口有 15 個掛了權限檢查。單獨查下去,八輪各找到一種漏法。要看的是「掛的那道問的問題對不對」,而且每個入口的檔名與行號要逐一列成驗收項,讀與寫分開數。


§9

報告寫給誰看,決定它有沒有用

這是過程中訂下的一條規矩,值得寫進來。

起因是有一份報告,把工具跑了多久、派了幾個 AI 放在最前面,要往下捲到第 150 行才看得到第一個問題。當時的評語是「連工程師都不想看」。

於是訂了規矩:

每個問題必須回答五件事

① 這是什麼問題 ② 出事會怎樣 ③ 壞人要先有什麼才做得到 ④ 在哪裡 ⑤ 怎麼修

報告順序固定

結論 → 查了什麼 → 總覽 → 細節 → 可信度 → 工具跑了多久放最後

還有一張對照表,規定技術詞要怎麼講。例如「TLS 憑證驗證被停用」要改寫成「連線的時候沒有確認對方是不是真的那個網站」。


§10

哪些地方我們不能說「查過了」

「還沒查的」和「查過確認沒事的」,意思完全不同。

前者是不知道,後者才是確認安全。這一節把兩者分清楚——有人問起的時候,我們要答得出來。

五種不能算查過的情況

情況 白話說明
① 從頭到尾沒排進來的面向 例如:存進資料庫的內容在畫面上怎麼顯示(那要查前端,兩次都沒查);防竄改機制的發證端、加密邏輯本身、出貨打包的流程
② 查證資源被「重複的舊問題」吃光 最誇張的一次:12 個疑點裡有 9 個是以前報過的,36 張票裡 27 張投在不該查的地方,真正投在該查的東西上只有 3 張——而那次真正重要的兩個問題,都是人自己找出來的
③ 沒有留下「每個檔案都看完了」的紀錄 最常見的一種。我們能說的是「這幾條問題確實存在」,不能說「這些檔案只有這幾條問題」
④ 現在沒事是靠運氣,不是靠設計 例如某個漏洞現在打不到,是因為程式的執行順序剛好擋住了——哪天有人整理程式碼把那個順序改掉,漏洞就回來了
⑤ 盤點本身就有死角 檢查「客戶資料有沒有隔開」的時候,只查了有記錄「這是哪個客戶的」那些資料表;連這個記錄都沒有的表,根本不在清單上

兩處整體性的保留

一、有一塊功能只能說「查了最關鍵的部分」

任務與成員管理那一塊,原本規劃七次、實際做了三次就停。剩下約 86 個檔案如果有別種類型的問題,這次不會被發現。

停下來是主動的取捨:同一個根本原因已經第三次出現,同樣的時間拿去查從來沒碰過的地方更有價值。

二、主系統自己的程式最後才查,有四條沒經過三票覆核

2026-09-20 曾經止損(檢視工具不可靠、繼續投入不划算),當時三塊完全沒碰;2026-09-21 決策者裁定重啟,換了上面講的兩個做法(跨兩個程式庫配對切、零發現輪靠實測)之後,那三塊與各模組的主系統接線在四天內查完。

主系統自己寫的那一層(826 個正式程式檔裡有判斷邏輯的 119 個,加上組裝設定)在 09-25~26 用 14 輪查完(見主系統自己的程式)。其中四條是執行者自己開檔找到、沒經過三票覆核,另有一輪工具一個疑點都沒提出——那幾處的可信度靠執行者逐支通讀與實測,只能說「查出的這些確實存在」。


§11

接下來看什麼

你想知道 看哪裡
整體結論、該先做什麼 總報告首頁
某一塊功能查了什麼、結果如何 〈逐項檢視結果〉
所有問題按嚴重程度排下來 〈問題總表〉