iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
AI Engineering

同一把尺,30 天橫評 AI Agent Skill:從單篇實測到一份能查的選用指南系列 第 6 篇

Day 6 | 資安掃描工具抓到了漏洞,也漏掉了一個更大的漏洞

  • 分享至 

  • xImage
  •  

前五天測的都是「產出品質」型的 skill——寫得好不好、抓得準不準、記得住記不住,錯了頂多重來一次。今天測的是 Trail of Bits——一間在資安圈子裡份量很重的研究公司,主業是幫別人的程式碼、區塊鏈協議、加密系統做稽核,平常的工作就是產出公開或半公開的稽核報告,找出真實系統裡的漏洞。他們把自己稽核工作裡會用到的自動化方法包成一組 Claude Code 的 skill,公開放在 github.com/trailofbits/skills。這種背景的公司願意把內部方法公開,本身是一個值得測的訊號:如果連他們包的東西都測不出什麼名堂,大概率不是他們的問題,是這條路本身有限制;如果測出真的有用或有缺口的東西,這個結論的份量也比隨便一個不知名作者的套件重。這是這系列第一次測資安類的 skill,跟前五天測過的東西比起來,這類工具的失敗方式不一樣:它不是「答案普通」,是「你以為安全,其實不安全」,中間那個落差平常沒人會提醒你,只有真的出事那天才會發現。如果一份掃描報告給你錯誤的安全感,那比它什麼都不說更危險,因為你會停止懷疑、停止繼續找其他方法把關。這種東西值不值得信任,比前五天測過的任何一個都更需要老實查證。

技能卡|static-analysis(Trail of Bits)

  • 名稱:static-analysis,Trail of Bits 官方出品,這次測的是裡面的 semgrep 子技能(同系列還有 semgrep-rule-creator、property-based-testing 等,今天沒測)
  • 來源:github.com/trailofbits/skills,公開倉庫;底層引擎是開源的 Semgrep,不是 Trail of Bits 自己寫的分析引擎
  • 觸發方式:不是關鍵字被動觸發,需要明確呼叫;正常流程裡有一個核准關卡——先讓使用者看過要掃哪些規則集、會拉進哪些第三方規則庫,確認過才正式開始掃
  • 規則組成:官方泛用規則集(p/security-audit、p/secrets)+ 語言框架規則(p/python、p/flask)+ Trail of Bits 自己維護的專屬規則庫 + 額外拉進其他資安公司的規則(這次實際看到 elttam、Apiiro——elttam 是澳洲一間做滲透測試與程式碼審查的資安顧問公司,Apiiro 是做應用程式安全風險管理的公司,都不是無名小廠)
  • 輸出契約:每個發現帶一個機器可讀的 check_id,全部規則集掃完後會合併成一份 SARIF 報告、自動去重,可以直接接 CI
  • 這次證據狀態:兩個合成案例,官方 skill 完整跑過一輪(不是手動模擬底層引擎),with/without 各 1 次

先搞懂它在做什麼

Semgrep 這類工具不是真的理解你的程式在做什麼,是拿一大套規則庫去比對程式碼的語法結構(有分析語法樹,不是單純字串比對),看有沒有符合已知危險模式的寫法。規則庫按語言、框架分類,每條規則都有一個 check_id,對應到一份說明「這個模式為什麼危險、怎麼修」。這種設計可重複、可自動化——同一段程式碼,不管誰在什麼時候跑,只要規則庫版本一樣,結果就一樣;但也因此它只認得規則庫裡寫過的模式,任何沒收錄的問題類型,不管多明顯,它都看不見。Trail of Bits 這個 skill 在這個基礎上疊了一層:自動判斷專案用什麼語言、該套哪些規則集,還拉進自己跟其他資安公司的規則庫——等於把「自己挑規則集」這件事自動化了,但疊再多層規則庫,語法比對這個方法本身的能力邊界不會變。

再往下一層講,Semgrep 抓到 SQL injection 這類問題,靠的是一種叫「污染追蹤」(taint tracking)的技術:先標出程式裡哪些資料是「來源」(source)——通常是使用者輸入,像網址參數、表單欄位;再標出哪些操作是「終點」(sink)——像直接執行 SQL、直接寫進檔案系統指令;只要有一條路徑能從來源一路不經過任何清洗(sanitize)就走到終點,就判定為危險。這個機制的好處是不會只看單一行程式碼,能追一小段資料流;壞處是它的「路徑」只能在單一檔案裡(沒有付費升級的跨檔功能),而且它認得的「終點」一樣是照規則庫寫死的清單——執行 SQL 是終點,但「把資料回傳給沒有驗證身份的呼叫者」,這種行為在它的世界裡不算是需要警戒的終點,因為規則庫裡沒人告訴它這也該算。

測試設計

兩個檔案都是很短的 Flask 小應用,只有一個 /user 路由:一個直接用字串拼接組 SQL 查詢,是教科書等級的 SQL injection;另一個功能一樣,但改用參數化查詢,把這個洞補起來了。選 SQL injection 當主題,是因為它是這類工具最擅長、也最經典的偵測目標,如果連這種都測不出差異,後面不用再往下測。第二個案例刻意不做成「完全沒問題的乾淨程式碼」,而是「修好了一個已知的洞,但可能還藏著別的問題」——現實世界很少有真正完美無瑕的程式碼,比較常見的狀況是修好一個漏洞後大家鬆一口氣,卻沒注意到旁邊還有別的問題。兩個案例都各跑一次完整的官方 skill 流程,另外派一組不用任何工具、單純讀程式碼的盲測基線當對照。

抓到漏洞:抓對了,但過程有一個容易忽略的細節

有 SQL injection 的那個案例,官方 skill 跑完五組規則集,合併去重後留下 1 筆發現,指向那行字串拼接組出來的 SQL 查詢,判定為 ERROR 等級,建議改用參數化查詢。盲測基線也抓到同一個問題,講法一樣具體。這輪兩邊都做對了。

SQL injection 之所以是這類工具最擅長的題目,是因為它的危險模式非常固定——使用者輸入透過字串串接直接進到執行 SQL 的函式,這條路徑幾十年來就是那幾種寫法,規則庫很容易寫得又準又全。這也是為什麼它明明是老掉牙的問題,規則庫抓得又快又準,卻還是三不五時出現在真實系統裡:不是工具抓不到,是很多程式碼根本沒被這類工具掃過,或者掃過但沒人去看報告、去修。工具擅不擅長是一回事,有沒有真的被用起來、有沒有人跟進處理,是另一回事,這點在讀今天這篇的時候也值得放在心上。

真正值得記的細節是:p/security-audit——這個名字聽起來最像「全面資安檢查」的規則集——這次是 0 命中,真正抓到問題的是語言框架專屬的 p/python 跟 p/flask。這代表挑規則集本身就有學問,「聽起來最全面」不等於「涵蓋最廣」,框架專屬的規則集反而常常是抓到真正問題的那一組。這個結果被驗證了兩次(先手動重建一次,再跑正式官方流程一次),兩次一致,不是抽樣誤差。

另外規則本身標的分類是 CWE-704,跟這個問題實際對應的 CWE-89(SQL Injection)對不太上——工具的 metadata 本身也不是百分之百精確,值得養成核對的習慣,不要照單全收。

這裡順便解釋一下規則集命名背後的邏輯,知道這個之後你自己選規則集會更有底。Semgrep 生態裡的規則集分成幾種層次:像 p/security-audit、p/secrets 這種是官方維護的「泛用」規則集,涵蓋很廣但不特別深入任何一種框架的細節;p/python、p/flask 這類是照語言或框架分的「專項」規則集,規模比較小,但因為只需要照顧一種生態,寫得比較深入、貼近該框架真實常見的用法;再上一層則是像 Trail of Bits 這樣的資安公司自己維護的「專屬」規則庫,通常是他們在真實稽核案子裡踩過的坑、抓過的漏洞類型,反過來寫成規則。三者不是誰取代誰的關係,是覆蓋範圍不同、互補的關係——今天的結果剛好示範了「泛用規則集不一定涵蓋所有框架的細節,專項規則集反而可能更準」這件事,不能單純假設「規則越多、規則集越大」就等於「掃得越完整」。

沒抓到的東西:今天最重要的發現

第二個案例(已經修好 SQL injection)跑完,七組掃描全部成功,唯一的發現是一則 INFO 等級的路由入口標記,不是漏洞。沒有誤判成有問題,這點是對的。

但同一個檔案,不用任何工具的盲測基線多抓到一件事:

「完全沒有身份驗證/授權(Broken Access Control)——/user?username=xxx 任何人都能直接查任何一個使用者的 email,沒有登入檢查、沒有『只能查自己』的限制。這是這支程式最大的風險:等於一個公開的使用者資料查詢端點。」

它還往下推了一層,指出這會導致使用者列舉(拿帳號字典去掃、靠回應有無差異確認哪些帳號存在)跟 PII 外洩的複合風險。

官方 skill 完全沒有提到這件事,即使動用了 Trail of Bits 自己的規則庫加上兩家第三方資安公司的規則,七組掃描沒有一組碰到這個問題。 這不是案例設計得特別刁鑽才踩到的例外——語法比對這類工具的規則庫是照著已知的程式碼層級漏洞模式寫的,「這支 API 沒有檢查呼叫者是誰」不是一個語法模式,是一個要理解「這支程式在做什麼、誰應該能呼叫它」才看得出來的商業邏輯問題,靜態分析工具沒有這個上下文,自然抓不到。連 skill 作者自己都清楚這條界線,文件裡明講某些情境要改用別的工具,代表這個方法的能力邊界從一開始就是已知的,不是這次測試才發現的意外。

這裡有一個諷刺的地方,值得多講一句:業界公認最權威的資安風險排行榜 OWASP Top 10,最新一版把「權限控管缺失」(Broken Access Control)排在第一位,注入類攻擊(包含 SQL injection)反而排到第三——也就是說,今天靜態分析工具抓得又快又準的那個問題,在真實世界的統計上,還不是最常見、危害最大的那一類;而它完全看不到的那類問題,反而是統計上排名最高的風險。這不代表 SQL injection 不重要,而是提醒你:一套工具能不能把你最該擔心的問題排在最前面,跟它掃描起來準不準、報告做得多漂亮,是兩回事。

靜態分析只是資安檢查的其中一種

把這件事放進更大的脈絡看會更清楚。業界通常把程式碼安全檢查分成幾種不同的方法,各自照顧不同的層面:SAST(Static Application Security Testing,今天測的這類)在不執行程式的情況下比對原始碼;DAST(Dynamic Application Security Testing)實際把程式跑起來、像攻擊者一樣送真實請求去戳,能抓到 SAST 看不到的執行期問題,包含一部分權限控管缺失;人工滲透測試與架構審查則是唯一真正會問「這個系統的權限模型設計得合不合理」這種問題的方法。Trail of Bits 自己身為一間資安顧問公司,正職做的其實是後面這兩種——今天測的 skill 只是他們把 SAST 這一塊自動化、公開釋出的部分,不是他們完整服務的全貌。理解這個定位,才不會誤以為裝了一個資安 skill,就等於把資安顧問公司的完整服務都請回家了。

這三種方法的成本結構也不一樣,值得放在一起比較:SAST 最便宜、最快、可以無限次重複跑,適合接進日常的開發流程當第一道防線;DAST 需要一個真的能跑起來的環境,成本高一截,通常排在上線前的固定檢查點;人工審查最貴、最慢,但也是唯一能問出「這個系統的權限模型合不合理」這種問題的方法,通常只在關鍵系統或大改版時才會動用。把 SAST 掃描工具當成免費、隨時可用的第一道濾網是合理的用法,但如果因為裝了這道濾網就跳過後面兩層,等於是把整個安全策略押在它看得到的那個範圍裡,這才是今天最想提醒的事。

也有一個小地方值得記:這次拉進的第三方規則庫裡,有些規則是針對 Java、JavaScript 這類其他語言寫的,掃這個純 Python 專案時直接編譯失敗被跳過。官方輸出裡有標記,但不會主動跳出來提醒你「這部分沒真的測到」,代表「拉了幾家知名資安公司的規則庫」不等於「每一條規則都真的對你的專案有效」,語言對不上的規則只是安靜地被跳過。

這對你有什麼用

如果你以後也想拿靜態分析工具幫程式碼把關,今天這個反差值得記住:它擅長的是「這段語法本身有沒有已知的危險模式」,不擅長「這支程式的存取邏輯合不合理」。 這兩類問題不是同一個工具能一次顧到的,就算背後掛了好幾家知名資安公司的規則庫也一樣。換個角度講,跑完這類工具看到「0 個發現」,比較安全的解讀是「已知的程式碼層級危險模式都沒踩到」,不是「這支程式很安全」——這兩句話聽起來很像,差別卻很大,今天的案例二就是活生生的例子:0 個漏洞發現,但這支 API 讓任何人都能查到別人的 email,兩件事同時成立,完全不矛盾。

具體一點,可以問自己兩層完全不同的問題:第一層是「這段寫法本身安不安全」——字串有沒有正確跳脫、有沒有用參數化查詢——這一層丟給這類工具去顧,效果穩定又划算;第二層是「這個功能存在的意義是什麼、誰應該摸得到它」——這一層工具幫不上忙,得靠人自己想清楚,或找真的懂系統業務邏輯的人看過一遍。混著問,兩層都顧不好。

幾個更實務的提醒:

  • 不要只挑一個聽起來最全面的規則集就收工。 今天的案例證明框架專屬規則集常常才是真正抓到問題的那組,泛用規則集反而可能漏掉。
  • 看到 check_id 或分類標籤,順手核對一下。 標錯分類的情況是存在的,工具的自我描述不是絕對可信的。
  • 掃出來的筆數先去重再看。 同一個問題被多個規則集各報一次是常態,數筆數之前先看是不是同一個位置的同一個問題。
  • 第三方規則庫拉進來,留意有沒有因為語言不符被跳過的部分。 執行紀錄裡通常有標記,但不會主動提醒你,掃完之後值得花一分鐘看一下有沒有哪些規則其實沒真的跑到。
  • 把這類工具當守門員,不要當唯一裁判。 接進 CI 擋下已知模式很划算,但「這支功能該不該對外公開」這種設計層級的問題,還是要靠人把關。
  • 每次接一支新 API,先問自己一句「誰應該能呼叫這個?」,再問「這段程式碼寫得安不安全」。 順序反過來,容易先被眼前的語法問題吸走注意力,反而漏掉更根本的設計問題——這也是今天案例二示範的教訓。
  • 拿到「0 個發現」的掃描報告,先確認掃的是什麼範圍、用了哪些規則集,再決定要不要放心。 一份看起來乾淨的報告,代表的是「已知模式都沒踩到」,不是「這支系統沒有安全問題」,兩者中間那道差距,得靠別的方法去補。

誠實交代這次測試的限制

  • 核准 ruleset 的硬性關卡,這次是直接授權往下跑,不是真的有人在互動介面上逐項核可——這個關卡的設計理念是好的,讓你在真正開始掃之前先看過要拉進哪些規則來源,真實使用時建議照它原本設計走完這一步。
  • 沒有跨檔案的資料流分析(需要另外開通的進階功能),今天的案例都只有單一檔案,這個限制今天影響不大,但如果你的專案是多檔案、跨模組的資料流問題,這點會是真正的差異來源。
  • 兩個案例都是刻意寫死、只有十幾行的簡化範例,跟真實世界動輒幾萬行、混雜多種框架版本的程式碼庫比起來,複雜度差了好幾個數量級。
  • 「靜態分析看不到權限問題」這個結論,我有把握是真的——這是這類工具本來就有的已知限制,不是巧合——但今天只驗證了一次,不能講成「這個工具完全沒用」這種過頭的結論。
  • 盲測基線理論上每次重跑用字遣詞會不一樣,今天只跑了一次,不能保證每次重跑都抓得到權限控管這個問題。
  • 兩個案例都圍繞同一種漏洞類型(SQL injection 有無)設計,沒有測到這類工具對其他弱點類型的表現,例如硬編碼密鑰、不安全的反序列化、命令注入——這些都是它規則庫裡涵蓋的項目,但今天沒有實際驗證過。
  • 「靜態分析工具能不能自動化守門」這件事,跟「一間資安顧問公司值不值得信任」是兩個不同層次的問題,今天只驗證了前者。Trail of Bits 作為公司本身的稽核品質、他們正式付費服務的深度,不是這篇文章能回答的範圍,這裡只針對他們公開釋出的這一支免費工具說話。

跟前面幾天放在一起看

這系列每天測的都是不同的 skill,但回頭看,一直反覆出現同一個結構:某個東西講得很篤定、很完整,但它篤定的範圍是有邊界的,邊界外面的事它完全不知道自己不知道。Day 4 是模型對訓練資料截止後的新寫法一無所知;Day 5 是通用建議不知道有現成模式可以直接沿用;今天是連掛了好幾家知名資安公司規則庫的官方掃描工具,也完全看不到一個商業邏輯層級的權限漏洞。三次的盲區位置都不一樣,但形狀是一樣的:工具或模型不會主動告訴你它看不到什麼,只會安靜地在它看得到的範圍內做到最好。回頭想想前五天測過的每一個 skill,其實都可以問同一句話:它講得很有自信的那個範圍之外,還有什麼是它看不到、但你需要知道的?資安這個領域把這件事講得特別清楚,因為它的盲區有真實代價——一份程式碼審查建議漏掉一個問題,頂多是少了一個優化機會;一份資安掃描報告漏掉一個問題,代價是系統真的被打穿。今天的教訓沒有比前幾天更特別,只是後果比較重。

今天的判斷

今天真正的收穫不是「Trail of Bits 這套工具能不能用」——它在自己擅長的範圍內表現得很扎實,抓到的 SQL injection 判斷正確、也沒有對乾淨的程式碼誤判,這點無庸置疑——是任何一種自動化檢查工具都有它看得到跟看不到的範圍,靠單一工具把關永遠會有一塊盲區,這塊盲區不會自己舉手說「這裡我漏看了」,只有拿別的方法去對照,才會知道它在哪裡。如果你只跑過這類工具、報告顯示乾淨,千萬別把這個結果當成「這支系統很安全」的證明——它只證明了「這支系統沒有踩到這套工具認得的那些模式」,這中間的距離,可能就是下一個真實漏洞藏身的地方。

明天想換一個完全不同的方向測,這系列接下來還是每篇一個獨立的技能卡,累積起來變成一份你自己就能查的 skill 選用清單。


上一篇
Day 5 | 我們自己在用的記憶外掛,這次換我來查它有沒有在唬爛
下一篇
Day 7 | 明明講對了完成的話,它卻沒有停下來
系列文
同一把尺,30 天橫評 AI Agent Skill:從單篇實測到一份能查的選用指南 共 11 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言