Go 如何緩解來(lái)自軟件依賴的惡意攻擊?
大家好,我是煎魚。
前段時(shí)間有一起比較嚴(yán)重的安全事故,引起了國(guó)內(nèi)外的集中關(guān)注、討論和走查。聽說(shuō)個(gè)別朋友在當(dāng)時(shí)都加塞了新的活,得加班加點(diǎn)檢查一下。
這一連串事件,不禁讓我思考到 Go 是如何解決和防護(hù)攻擊相關(guān)的問(wèn)題。周末翻到了《How Go Mitigates Supply Chain Attacks[1]》,內(nèi)容物就是針對(duì) Go 如何解決來(lái)自軟件依賴的惡意攻擊是相關(guān)的?;诖苏砗驼{(diào)整后分享給大家。
安全事故:xz 核彈后門
這是今天內(nèi)容的行業(yè)背景,如果沒(méi)提前了解的同學(xué)可以看看。@ZainanZhou 大佬的對(duì)這件事的概述。
如下圖:
圖片
什么是供應(yīng)鏈攻擊
現(xiàn)代軟件工程是協(xié)作的,并且基于開源軟件(例如:GitHub)。這種做法使得目標(biāo)容易受到供應(yīng)鏈攻擊,即通過(guò)破壞軟件項(xiàng)目的依賴關(guān)系來(lái)進(jìn)行攻擊。
通過(guò)軟件依賴項(xiàng)進(jìn)行攻擊
盡管有任何流程或技術(shù)措施,每個(gè)依賴關(guān)系都不可避免地存在信任關(guān)系的疑慮。在這一塊領(lǐng)域,Go 工具做了許多的設(shè)計(jì),有助于在不同階段降低攻擊的風(fēng)險(xiǎn)。
Go 對(duì)攻擊的防護(hù)措施
所有構(gòu)建都是 “鎖定的”
外部世界的變化(例如:依賴項(xiàng)的新版本發(fā)布),不會(huì)自動(dòng)影響 Go 的構(gòu)建過(guò)程。
與其他大多數(shù)軟件包管理器文件不同,Go 模塊沒(méi)有單獨(dú)的約束列表和鎖定特定版本的鎖文件。任何 Go 構(gòu)建所依賴的每個(gè)版本都完全由主模塊的 go.mod 文件決定。
自 Go 1.16 版起,這種確定性被默認(rèn)執(zhí)行,如果 go.mod 文件不完整,編譯命令(go build、go test、go install、go run 等)將會(huì)失敗。
唯一會(huì)改變 go.mod(進(jìn)而改變編譯)的命令是:go get 和 go mod tidy。這些命令不會(huì)自動(dòng)運(yùn)行,也不會(huì)在 CI 中運(yùn)行,需要人為控制。因此對(duì)依賴關(guān)系樹的修改必須經(jīng)過(guò)深思熟慮,并有機(jī)會(huì)通過(guò)代碼審查發(fā)現(xiàn)。
這一點(diǎn)對(duì)安全性非常重要,因?yàn)楫?dāng) CI 系統(tǒng)或新機(jī)器運(yùn)行 go build 時(shí),已簽入的源代碼是最終和完整的真實(shí)來(lái)源(即什么將被構(gòu)建)。第三方無(wú)法對(duì)此產(chǎn)生影響。
此外,當(dāng)使用 go get 添加依賴關(guān)系時(shí),其傳遞依賴關(guān)系會(huì)按照依賴關(guān)系的 go.mod 文件中指定的版本添加,而不是按照它們的最新版本添加,這要?dú)w功于 go mod 的最小版本選擇設(shè)計(jì)。
即使調(diào)用 go install example.com/cmd/devtoolx@latest也需要符合上述條件。在某些生態(tài)系統(tǒng)中,其配置的等效項(xiàng)會(huì)繞過(guò)固定版本。
在 Go 中,example.com/cmd/devtoolx 的最新版本將被獲取,但所有依賴關(guān)系將由其 go.mod 文件設(shè)置版本控制。
也就是說(shuō),如果某個(gè)模塊被入侵,并發(fā)布了新的惡意版本,在明確更新該依賴關(guān)系之前,任何人都不會(huì)受到影響,這就為生態(tài)系統(tǒng)提供了審查變更的機(jī)會(huì)和檢測(cè)事件的時(shí)間。
版本內(nèi)容永不改變
確保第三方無(wú)法影響構(gòu)建所需的另一個(gè)關(guān)鍵屬性是模塊版本的內(nèi)容不可更改。如果攻擊者破壞了某個(gè)依賴項(xiàng),并重新上傳了現(xiàn)有版本,那么他們就會(huì)自動(dòng)破壞所有依賴于該依賴項(xiàng)的項(xiàng)目。
圖片
Kubernetes go.sum
這就是 go.sum 文件的作用。它包含了每個(gè)依賴項(xiàng)的加密哈希值列表,這些依賴項(xiàng)都對(duì)構(gòu)建有貢獻(xiàn)。并且 go.sum 文件不完整會(huì)導(dǎo)致運(yùn)行錯(cuò)誤。
現(xiàn)階段 go.sum 文件只有 go get 和 go mod tidy 會(huì)修改它,因此對(duì)它的任何修改都會(huì)伴隨著有意的依賴關(guān)系變更。其編譯保證有完整的校驗(yàn)和。
有了 sumdb(Checksum Database,簡(jiǎn)稱 sumdb),被破壞的依賴關(guān)系,甚至谷歌運(yùn)營(yíng)的 Go 基礎(chǔ)架構(gòu)都不可能用修改過(guò)(如被回溯過(guò))的源代碼來(lái)攻擊特定的依賴關(guān)系。你所使用的代碼與其他正在使用 example.com/modulex v1.9.2 并已審核的人所使用的代碼完全相同。
VCS 是真相之源
大多數(shù)項(xiàng)目都是通過(guò)某種版本控制系統(tǒng) (VCS) 開發(fā)的,然后在其他生態(tài)系統(tǒng)中上傳到軟件包倉(cāng)庫(kù)。
圖片
最常見:git 和 svn
這意味著有兩個(gè)賬戶可能被入侵,一個(gè)是 VCS 主機(jī),另一個(gè)是軟件包存儲(chǔ)庫(kù),后者使用得更少,更容易被忽視。
這也意味著在上傳到軟件倉(cāng)庫(kù)的版本中更容易隱藏惡意代碼,特別是如果源代碼在上傳過(guò)程中經(jīng)常被修改,例如為了最小化惡意代碼。
在 Go 中,不存在軟件包版本庫(kù)賬戶。軟件包的導(dǎo)入路徑包含 go mod 下載所需的信息,以便直接從 VCS 獲取模塊,而 VCS 中的標(biāo)簽定義了版本。
Go 模塊存在一個(gè)鏡像代理(例如:goproxy.cn),其在安全上有一個(gè)精妙之處:代理上的 go 工具在一個(gè)強(qiáng)大的沙箱中運(yùn)行,并被配置為支持所有 VCS 工具,而默認(rèn)情況下只支持兩個(gè)主要的 VCS 系統(tǒng)(git 和 Mercurial)。
使用代理的用戶仍然可以獲取使用非默認(rèn) VCS 系統(tǒng)發(fā)布的代碼,但在大多數(shù)安裝中,攻擊者無(wú)法獲取這些代碼。
構(gòu)建代碼不會(huì)執(zhí)行代碼
Go 工具鏈明確的安全設(shè)計(jì)目標(biāo)是:無(wú)論是獲取代碼還是構(gòu)建代碼,都不會(huì)讓代碼執(zhí)行,即使它是不受信任的惡意代碼。
這一點(diǎn)與大多數(shù)其他生態(tài)系統(tǒng)不同,許多生態(tài)系統(tǒng)都能在獲取軟件包時(shí)運(yùn)行代碼,并且支持的很好。但現(xiàn)實(shí)上,黑客常常會(huì)將受攻擊的依賴關(guān)系轉(zhuǎn)化為受攻擊的開發(fā)者機(jī)器,并對(duì)模塊作者進(jìn)行各種危險(xiǎn)的攻擊。
在 Go 中,不為特定模塊進(jìn)行構(gòu)建和運(yùn)行代碼的話,不會(huì)對(duì)其產(chǎn)生安全影響。
少量復(fù)制勝過(guò)少量依賴
在 Go 生態(tài)系統(tǒng)中,最后也可能是最重要的軟件供應(yīng)鏈風(fēng)險(xiǎn)緩解措施是最沒(méi)有技術(shù)含量的:Go 有一種拒絕大型依賴關(guān)系樹的文化,這種文化傾向于少量復(fù)制,而不是添加新的依賴關(guān)系。
這可以追溯到 Go 的一個(gè)諺語(yǔ):“a little copying is better than a little dependency[2]”。高質(zhì)量的可重用 Go 模塊自豪地貼上了 "零依賴" 的標(biāo)簽。
如果你發(fā)現(xiàn)自己需要一個(gè)庫(kù),你很可能發(fā)現(xiàn)它不會(huì)導(dǎo)致你依賴其他作者和所有者的數(shù)十個(gè)其他模塊。
這也得益于豐富的標(biāo)準(zhǔn)庫(kù)和附加模塊(golang.org/x/......),它們提供了常用的高級(jí)構(gòu)建模塊,如 HTTP 棧、TLS 庫(kù)、JSON 編碼等。
這些意味著,只需少量的依賴關(guān)系,就能構(gòu)建豐富、復(fù)雜的應(yīng)用程序。無(wú)論多么優(yōu)秀的工具都無(wú)法消除代碼重用的風(fēng)險(xiǎn),因此最有力的緩解措施永遠(yuǎn)是小型依賴關(guān)系樹。
總結(jié)
Go 這一門編程語(yǔ)言背靠 Google,其在企業(yè)內(nèi)部有一定規(guī)模的使用了他。因此 Go 在這幾年中,Go 核心團(tuán)隊(duì)的目標(biāo)之一就是緩解供應(yīng)鏈的攻擊,確保 Google 自身的軟件體系較為安全可靠。
今天我們了解 Go 模塊管理內(nèi)的各種依賴庫(kù)的管理機(jī)制等,其通過(guò)鎖定構(gòu)建的內(nèi)容和版本、為 VCS 打造沙箱、構(gòu)建工具的安全化等綜合手段,為 Go 提供了保障護(hù)航。
參考資料
[1]How Go Mitigates Supply Chain Attacks: https://go.dev/blog/supply-chain
[2]a little copying is better than a little dependency: https://www.youtube.com/clip/UgkxWCEmMJFW0-TvSMzcMEAHZcpt2FsVXP65