軟體供應鏈安全聽起來像是設有專職資安部門的大型組織才需要煩惱的事,而正是這種定位讓人判斷失準。一個只維運少數幾項服務的小型團隊,通常同樣相依於數百個套件,而其中沒有任何一個是團隊裡有人真正讀過的。這些套件會在建置時從團隊無法掌控的套件庫抓下來,接著在存放部署認證資訊的機器上執行安裝腳本。

暴露程度並不跟著公司規模等比例放大。它跟著相依套件的數量與建置自動化的程度放大,而小型團隊往往是前者更多、後者更少人盯著,處境比那些公開談論這件事的大型組織還要吃緊一些。

令人不舒服的算術: 你的應用程式大概有十來個直接相依套件,以及數百個間接相依套件。那十來個是你自己挑的。其餘的不是,你一個也沒讀過,而其中任何一個只要執行安裝腳本,拿到的權限就跟你的建置程序一模一樣。這才是真正的攻擊面,而在你親手寫下的那份相依性清單裡,它根本看不出來。


供應鏈風險從安裝那一刻就開始

危險的時刻通常不是執行程式碼,而是安裝程式碼。

套件管理工具允許安裝過程中執行腳本,這表示一個被入侵或本身就帶惡意的套件,會以執行安裝指令那個人的權限直接跑起來。在開發者的機器上,那是他本人的認證資訊。在建置流水線裡,那是部署金鑰,後者的後果嚴重得多。

真實發生過的事故,絕大多數可以歸進三種攻擊型態。

typosquatting,也就是仿冒近似套件名稱。 攻擊者發布一個名字只跟熱門套件差一個字元的套件,然後就在那裡等著有人把安裝指令打錯。佈置成本極低,而母數一大就一定有人踩到。

合法套件的帳號遭盜用。 一個被廣泛使用的套件,維護者的認證資訊被竊,接著被塞進有害內容後重新發布。這一類最難防,因為不論是套件名稱還是下載次數,都看不出任何異常。

相依性混淆。 攻擊者用你內部套件的名稱在公開套件庫發布同名套件,而設定錯誤的解析器偏偏優先選公開套件庫而不是你的私有套件庫。它徹頭徹尾是設定問題,也徹頭徹尾防得住。

這三種攻擊都不需要有人特地鎖定你,這正是重點所在。它們是機會主義的,而且可以無限鋪開。

在小規模下真正划算的控制

把鎖定檔提交進儲存庫,並且從鎖定檔安裝。 鎖定檔固定的是精確版本與對應的雜湊值。若改從相依性清單檔安裝,建置時就會重新解析出新版本,建置因此無法重現,上游的一處改動也會未經任何審查就抵達正式環境。請使用在鎖定檔對不上時直接失敗的安裝指令,而不是那個會默默把鎖定檔改掉的指令。

能關掉安裝腳本的地方就關掉。 許多生態系都提供這個選項,而大多數套件本來就不需要安裝腳本。這是在不引進任何額外工具的前提下,你能取得的最大一次暴露面削減;而少數因此跑不起來的套件,剛好就是你應該心裡有數的那幾個。

把漏洞警示自動化,接著做分流處理。 掃描器產出的結果遠遠超過一個小型團隊處理得完的量,而失效的方式並不是漏掉某一則警示,而是因為絕大多數與自己無關,索性把所有警示都當作沒看到。請先篩出那些確實能從你自己的程式碼被觸及的問題,只處理這一部分。一條沒人看的佇列,比根本沒有佇列還糟。

建置工具同樣要釘死版本。 容器映像檔、執行環境版本,以及流水線呼叫的那個動作或外掛,全都是相依項目。只要引用的是可變動的標籤而不是不可變的摘要值,你的建置就會在你不知情的情況下改變。

把建置用的認證資訊跟其他一切分開。 一條有權部署的流水線,不該同時握著能做更多事情的認證資訊;而一次被入侵的建置,也不該有能力碰到正式環境的資料。

SBOM 能告訴你什麼,不能告訴你什麼

軟體物料清單,簡稱 SBOM,列出的是你的應用程式裡究竟裝了些什麼,它的價值就在於能迅速回答一個問題:這件事影響到我們了嗎?

這個問題以前要花上好幾天才答得出來。當一個被廣泛使用的元件被爆出漏洞時,手上有一份最新清冊的組織幾分鐘就能給出答覆,沒有清冊的組織則要花一整週翻查。這個落差就是全部的理由,也正是各方推動採用 SBOM 的原因:它被視為基本實務,而不是什麼進階的控制措施。

它做不到的,是讓你變得安全。清冊終究是清冊,不是防禦。它不會告訴你上面列出的漏洞在你的程式碼裡是否可觸及,不會告訴你它在你的設定下是否真的要緊,也不會告訴你是否已經有東西被入侵。產一份出來然後歸檔了事的團隊,只是多了一份文件,而不是多了一項控制。

請從建置流程裡產生它,這樣它描述的才會是真正出貨的內容,而不是相依性清單檔所宣稱的內容。每一個發行版本都要留下與之對應的那一份。同時也要接受一件事:它的全部價值,都體現在你被追問時能多快給出答案。

小型團隊真正吃虧的地方

很少是因為某個罕見的套件遭到入侵,而是因為一些再普通不過的事。

留在儲存庫裡的機密資訊,刪掉之後仍然留在歷史紀錄裡,推送後幾分鐘內就會被自動化掃描器找到。一個停在三年前的相依套件,已經有公開的攻擊程式碼,卻沒有可行的升級路徑,因為升級一拖再拖,拖到本身變成一個專案。權限過大的建置流水線,從 fork 出來的儲存庫送來的合併請求,能以本來不該擁有的權限執行。沒有人在看,於是入侵長期存在,因為沒有任何一則警示最後送到活人面前。

這個結論一點也不好看:在實務上,把相依套件維持在最新狀態,就已經構成供應鏈安全的絕大部分。團隊當成可選項的軟體維護成本,指的正是同一件工作,而拖延它,會把一次例行升級變成一場附帶現成攻擊程式碼的緊急事故。

與規模相稱的起點

對一個小型團隊來說,照這個順序做:先把機密資訊從儲存庫清出去,凡是曾經外洩的一律更換。接著把鎖定檔提交進儲存庫,並嚴格從中安裝。打開相依套件警示,改成每週集中分流而不是隨時盯著。把建置工具釘死在不可變的參考上。把流水線權限收斂到必要的最小範圍。等這些都到位之後,再為每個發行版本產生一份 SBOM。

這個順序在沒有專職資安部門的情況下,也能涵蓋現實中真的會遇到的威脅,而且每一步花的是幾個小時,不是幾個星期。再往上的那些控制,例如來源證明、可重現建置、簽章過的產出物,價值都是實實在在的,但它們屬於基礎已經穩定運作之後的階段。

Mecanik 會在應用程式安全分析服務中檢視並強化建置流水線。我們查出來的東西幾乎從來不是什麼精巧的入侵,而是儲存庫裡躺著的一枚權杖,以及一個自從被選進專案那天起就再也沒人更新過的相依套件。



常見問題

為什麼安裝套件才是有風險的那一刻? 因為套件管理工具允許腳本在安裝過程中執行,而且是以執行安裝指令那個人的權限執行。在開發者機器上,那就是他本人的認證資訊;在建置流水線裡,那就是部署金鑰。這段程式碼根本不需要被你的應用程式呼叫,就已經足以造成破壞。

什麼是相依性混淆? 這是一種攻擊:攻擊者用你內部套件的名稱在公開套件庫發布同名套件,而設定錯誤的解析器優先選了公開套件庫而不是你的私有套件庫。它不需要有人特地鎖定你,而且只要把解析器設定改對,就能完全避免。

SBOM 實際上能帶來什麼? 它能對一個問題給出快速答案:這件事影響到我們了嗎?手上有最新清冊,幾分鐘就夠;沒有的話,得花一整週翻查。它不會告訴你列出的漏洞在你的程式碼裡是否可觸及,也不會告訴你是否已經有東西被入侵,所以一份產出後就歸檔的 SBOM,只是文件而不是控制措施。

我應該把鎖定檔提交進儲存庫嗎? 應該,而且要嚴格從鎖定檔安裝,用那個在對不上時直接失敗的指令,而不是會把它改掉的指令。從相依性清單檔安裝會在建置時重新解析出新版本,讓建置變得無法重現,也會讓上游的一處改動未經任何審查就進入正式環境。

小型團隊應該從哪裡開始? 先把機密資訊從儲存庫清出去並更換所有曾經外洩的憑據,把鎖定檔提交進儲存庫並嚴格從中安裝,打開相依套件警示並每週分流,把建置工具釘死在不可變參考上,收斂流水線權限,然後為每個發行版本產生一份 SBOM。每一步花的都是幾個小時,不是幾個星期。