博客 / 詳情

返回

2025年的 Crate 安全:工具與技術 (RustConf China 2025系列精選)

本內容是對 RustConf Chian 2025系列演講中 2025年的 Crate 安全:工具與技術 內容的翻譯與整理。推薦點擊鏈接觀看原視頻。

演講嘉賓 Adam Harvey, Rust基金會安全軟件開發者




好的,謝謝。大家好,我是 Adam。我大概會待在這邊,儘量不擋住屏幕。今天我要跟大家聊聊供應鏈安全。我在加拿大工作。

我在 Rust 基金會擔任軟件開發工程師,主要專注於生態系統安全。實際工作中,主要是嘗試在 crates 裏發現惡意軟件,並協助處理一些事件,比如昨晚有些人可能看到的,有人試圖冒充 crates.io 來竊取大家的 GitHub 令牌。那挺“有趣”的,導致我比預期更晚才睡。我同時也是 Rust crates.io 團隊的成員。我應該説明一下,儘管我今天穿着這件 T 恤,我並不是以 Rust 基金會的官方身份發言。但如果我説了什麼很聰明的話,那肯定就是 Rust 基金會的官方立場。

我們會比較快地過一遍,因為這是個相對簡短的演講。我會覆蓋兩個大的方面:第一,整體談談供應鏈安全,以及我所知為改進供應鏈正在進行的工作;第二,基於這些,介紹一些你可以實際使用的工具,幫助提升你自身供應鏈安全,改善你所依賴的依賴項的安全性。換句話説,演講的前半段我會把你們嚇一嚇,後半段我會告訴你們其實一切都會好起來的。

供應鏈安全的基本概念並不難。你所使用的一切,尤其在 Rust 這樣的語言裏,都會使用到其他東西,而那些東西又依賴別的東西;你構建的東西要安全,整條鏈上的東西都需要安全。實際上,幾乎不可能在實踐中完全避免這種鏈式依賴。即便你在做嵌入式系統開發,就像 James 剛剛講的那樣,即使你不使用標準庫,你仍然要依賴編譯器,可能還要依賴操作系統,而這些同樣需要是安全的。

cargo tree -p serde

舉個例子,這是一個簡單的 crate:Serde,大多數人應該都知道。即便是 Serde 也有 6 個依賴,而且我很確定 David Tolnay 已經盡力把依賴數降到最少了。這意味着,當我們使用 Serde 時,並不是只拉一個 crate,而是拉進來加總起來大概六七個之類的。這會很快累加,因為有些東西比另一些更復雜。


比如這個文本編輯器 Zed,有些人可能聽説過,它完全用 Rust 編寫。Zed 直接依賴(屏幕上有)164 個 crate,但當你查看整個依賴樹時,會拉進來 1115 個依賴。這就意味着有 1115 個 crate 必須是安全的。我就不在這裏展示 cargo tree 的輸出了。我嘗試過把它畫成圖,但效果並不好。重點是:依賴真的很多。

即便是更小的項目,比如 BAT(一個用 Rust 寫的更好的 cat 替代品),也有 30 個直接依賴和 119 個傳遞依賴。其中很多是我在工作中常見的,比如做命令行解析幾乎都會用到 clap。我的工作經常是寫一些一次性分析用的小工具。我會説,這些項目平均都會拉進 80 到 100 個依賴。這還不包括任何異步相關的內容,比如 Tokio 等等。總之,東西很多。

我在這裏提出三個判斷。

第一,你引入了大量你不控制的代碼。我在概括。就 Serde 而言,David Tolnay (譯者注: 知名Rust程序員, syn 和 quote等庫的作者)可能確實掌控了它所有的依賴,但如果你引入的是 clap 和其他東西,那就未必了。

更糟的是,你沒有讀過這些代碼。我肯定沒有。再拿 clap 舉例,它大約有 9.6 萬行 Rust 代碼。我見過 Ed Page (譯者注: clap的作者) 幾次,我相信他寫的是好代碼,但我不會假裝説我對它進行過詳細的審查。我大概也就為了弄明白幾個邊角問題,打開過一兩次代碼庫。

就像在座的大多數人一樣,我並不是純粹為樂趣寫 Rust。寫 Rust 是很有趣,但我寫它是因為工作,而且有截止日期。這意味着我沒有時間深入 119 個依賴並逐個審查所有代碼庫。現實不是那樣運作的。

為什麼這很重要?我不會深入回顧供應鏈安全攻擊的歷史。過去幾年我們見到過各種例子,從地下室裏的孩子到可能是國家級的攻擊者,以及介於兩者之間的各種人,都在嘗試入侵敏感系統。去年(最有名的)就是 XZ 的事件。我不打算重述歷史,你可以自己去搜,我猜大多數人現在已經聽説過了。

另一個例子是“打錯包名”(typosquatting):Python 裏有個常用包叫 Fabric,有人搞了一個叫 Fabrice 的包,末尾多了一個 e。這在一些歐洲國家是常見名字,他們的企圖就是讓人打錯字。有人可能因為打 Fabric 太多次了,裝錯了包。結果他們的 AWS 密鑰就被髮給了寫這個包的人,後續大概就發生了不好的事。類似的東西很多。我舉這個例子,是因為等會兒我會給大家看一些 Rust 惡意軟件的例子,但總體來説,形式多種多樣。

稍微有點好消息的是,這是一類大公司非常關心的問題,因為他們需要自己的代碼是安全的。所以有不少項目(包括我自己的崗位)是在被資助的前提下開展,目的就是發現這些問題並進行防禦。現在有相當多的工作在推進這些緩解措施。幾個月前一個已經上線的東西叫“Trusted Publishing”(可信發佈),在 crates.io 上。感謝資助該工作的 Alpha Omega (譯者注: 2022 年 2 月成立的一個項目, "其使命是通過促進最關鍵的開源軟件項目和生態系統的可持續安全改進來保護社會。該項目旨在構建一個關鍵開源項目安全、安全漏洞被快速發現和修復的世界" )。它允許你把整個 crate 的發佈過程完全放在 CI 中進行。目前只支持 GitHub Actions,但計劃將來支持其他平台。當然,以我作為 crates.io 團隊成員的身份來説,我們非常歡迎外部貢獻。

它的意義在於:你在 CI 中使用短期有效的臨時憑據發佈,從而消除一種風險---一旦有人拿到了密碼或 API Key 就能永久使用。如果 API Key 只有效 15 分鐘,且只存在於 CI 系統中,那麼攻擊者能夠利用的窗口就非常小了。

還有一個在進行中的項目,是使用 TUF 這套可信鏈來為 crates 和 Rust 發行版做簽名。共同作者 Josh Triplet 今天也在現場,你可以找他聊聊。這會解鎖很多事。短期內,一個關鍵能力是:可以把 crates 和 Rust 發行版安全地鏡像到那些不能不受限訪問互聯網的環境裏。長期看,它也將為 crate 作者發佈帶有證明(attestation)的 crate 提供基礎組件,用以聲明“這確實是我打算髮布的東西”,並建立信任鏈。

這個領域有很多機構在活躍。我在幻燈裏放了幾個:前面提到的 Alpha Omega,他們對多個生態(包括我們)提供大量安全工作的資助;Rust 基金會本身也覆蓋了一些工作;OpenSSF 是 Linux 基金會的一個子項目,也提供資助並作為很多工作的公共協作平台;Ecosystems 也在做很多相關分析。以上是理論部分。

接下來談談更實際的:我們能做什麼?有一個已提到的點是:如果你在發佈 crates,可以用 Trusted Publishing 改善你的發佈流程安全性。正如前面説的,“把依賴的每一行代碼都讀一遍”的樸素方法在實踐中大概率行不通。除非你讀得飛快而且比我更有時間,但大概也不現實。既然做不到,那我們還有哪些數據和工具,能幫助我們在往 cargo.toml 添加依賴時做出更明智的決定?

當然,你也可以選擇不依賴外部東西。這也是一種有效選擇。我自己也常常犯這個毛病:很容易就“cargo add 一個東西”,因為更快更省事。所以有時候我們應該退一步,想想這是否真的是應該做的。不過,當我回想為什麼用 Rust、為什麼開始用 Rust,標準庫和 crate 生態是很重要的原因之一,因為我不想再自己從零實現一個哈希表之類的。需要記住這一點。但在實踐中,我們大多數時候還是會加依賴。如果決定要加依賴,那我們該看什麼?遺憾的是,沒有一個“這東西絕對安全”的權威指標可以指給你看。但有一些線索,不過其中一些可能會誤導。

比如,我在 crates.io 上搜了 “async”,按“歷史總下載量”排序。因為 crates.io 的搜索引擎不算好,前兩個結果其實沒太大用。但看受歡迎程度也還行,Tokio 出現了,如果你做異步,初步看這大概就是你想要的。但下載量可以被操縱。攻擊者可以用他們從前面那個“打錯包名”裏竊取到的 AWS 密鑰搭服務器刷下載量。因此這是個很可能誤導的信號。不是完全沒用,但不是強信號。

我們還能看其他東西。有一些努力在嘗試對 Rust 生態裏的 crate 做分類,並標註“推薦、廣泛使用、高質量”等。這是一個叫 blessed.rs (譯者注: Rust 生態系統的非官方指南, 憤懣別類推薦了很多Rust生態的庫) 的站點,網址就是那個。我挺喜歡。還有別的,比如 awesome Rust,還有一些更聚焦的清單,如 async、embedded 等。這些都可能是很有用的工具,但它們的可信度取決於來源。

一旦你決定把某個 crate 加進依賴,你當然也需要知道它依賴了什麼、它的依賴又依賴了什麼。一種方式是生成 SBOM(軟件物料清單)。有多種工具,我不打算展開理論。cargo 本身也能告訴你會拉進來什麼。有時候這就夠你開始梳理了。比如,我們可以拿 cargo tree 的輸出,然後統計在該項目依賴圖中出現次數超過一次的常見依賴。

結果是:proc-macro2 出現 30 次,serde、quote、syn、tokio 等。這能讓你大致知道:如果我要評估安全性,優先看這些“高頻”依賴也許更有意義。

當你識別出一些想要仔細看的 crate 後,你需要決定什麼對你重要,以及如何分析。接下來是我在看依賴時關注的點,這不是放之四海而皆準的標準,很多人可能會不同意其中一些,這沒關係。重點在於方法要系統、一致。我會逐一展開。總體上,我關注:代碼質量、安全歷史、開發實踐、可持續性。

先看代碼質量。根本上説,沒有什麼能替代真正讀代碼、做評審。但這需要大量時間。那麼我們還有什麼選擇?我覺得“分佈式代碼評審平台”的理念很有意思。想法是:別人也有同樣的問題。有人在使用某個依賴之前可能會審查它,那麼這份評審結果就可以被以一種形式發佈出來,供其他人蔘考。你可以決定你有多信任這份評審,但至少它提供了額外數據,我們能“站在別人的肩膀上”。在 Rust 生態裏,我知道兩個這樣的工具:cargo-vetcargo-crev,可能還有我不知道的。實踐上 cargo-vet 的採用更廣一些,我主要以它為例。

基本思路是:你做完代碼審查,就把結果發佈出來。這是 cargo-vet 的格式,一個很簡單的 TOML 格式。比如這是來自 Google 的源。每個評審主體(通常是一家公司)發佈自己的評審。例子裏是對 ahash crate 的審計,版本 0.8.3。你可以指定評審標準,cargo-vet 內置了幾個,比如 safe-to-run(顧名思義)以及 Google 自己的“does-not-implement-crypto”。你可以據此得出:“有人審過 ahash 0.8.3,認為安全可運行”。

你對它的信任程度取決於你對 Google 做這件事的信任程度。

好在 cargo-vet 允許你按來源做信任決策。

cargo-vet 還帶一個命令行工具,我今天沒有時間演示,但挺好用,建議去看看。這些數據也會在 lib.rs 上展示(它是 crates.io 的替代前端)。比如對 Serde,當時最新的 1.0.219 得到了“safe-to-deploy、does-not-implement-crypto、UB risk 2(Google 對“低不安全破壞風險”的表達)”等標註。這有幫助,至少可以讓你決定:也許這個 crate 不需要逐行審查。

當然,如果你決定逐行審查,你得決定審什麼。我個人首先看這些:任何 unsafe 的使用、build.rs 腳本、過程宏(proc-macros)。對我來説,這些地方最可能隱藏惡意內容。尤其是 unsafe,很微妙;這方面我也會盡量依賴他人的評審結果。


但有個坑:如果你要審 Rust 代碼,前述的 build 腳本和過程宏會在你打開它時運行(比如在使用 Rust Analyzer 的編輯器或 IDE 裏)。這讓安全審計變得困難:因為你一方面可能需要一定程度的"代碼智能"功能(如語法分析、提示等),但又不希望如果某個 build 腳本被塞了惡意代碼,你的機器就被攻陷。有一些折衷方案:VS Code 可以用受限模式打開,但那樣你無法使用"代碼智能";

你可以在 docs.rs 上看源碼,這其實經常很有用,因為它展示的是 crate 包裏實際發佈的內容,而不是倉庫裏可能不同步的內容;你也可以去看倉庫,但它不一定與發佈包匹配。lib.rs 會告訴你倉庫和發佈包是否匹配。我們在 Rust 基金會內部也有這方面的跟蹤,我很慚愧的是過去一年沒來得及把它做成公開看板,但我會的,我保證。

這是 diff.rs,它展示兩個 crate 版本之間的差異。比如 Serde 1.0.216 與 1.0.217 的差異。如果你之前已經做過某個版本的審查,這就提供了一個只看“改了什麼”的方式。GitHub 也能看 diff,但依然未必和發佈包匹配。


第二,安全。Rust 生態有 RustSec,這是 Rust 和 crates 生態已知安全通告的數據庫。由 Rust 項目下的 Secure Code 工作組維護。它能給你任何有安全通告的 crate 的安全歷史。比如我拿 Hyper 舉例(抱歉了 Hyper)。最近一條在 2022 年,還有 2021 年的。點進去之後,內容和你在 CVE Advisories 裏熟悉的內容差不多。有受影響的版本。可能最重要的是,它告訴你“不受影響的版本”。所以如果你的依賴鏈裏沒有低於 hyper 0.14.12 的版本,那麼至少這個問題你是安全的。

RustSec 還提供了一些很好用的工具來消費這些數據。Secure Code 工作組自己有 cargo-audit;還有第三方的 cargo-deny,功能相似但更多。我非常強烈建議你使用其中一個工具(可能 cargo-deny 更好,因為它能做得更多),把它放進 CI。這樣當你更新依賴時,它會立刻告訴你是否有已知漏洞、有哪些許可證(如果你關心合規)、依賴從哪裏下載等等。如果今天只能帶走一件事,那就是:如果你在做 Rust 項目,把這些工具之一放進 CI。


第三,更加模糊的“開發實踐”。從項目外部判斷一個項目的開發實踐真的很難,最好的方法是親自參與。




當然也有一些指標可看。OpenSSF 有“安全記分卡”(Security Scorecards)。我不太相信用一個生硬的分數去量化一個項目的健康程度和安全實踐,但我們可以看看它測什麼,看看能不能“擇其善者而從之”。OpenSSF 自己不發佈記分卡,但 Google 有 deps.dev,基本上提供了這些數據,並且覆蓋所有 Rust crates。我挑了一個流行 crate(不告訴你是哪個),看了它在 2025-02-10 的記分卡,綜合得分 5.9/10。正如我説的,我不太在意總分,但我們看下分類:Dangerous Workflow(危險工作流)一項顯示該倉庫未檢測到危險的 GitHub Actions 工作流,這很好,10/10;同時它被認為是“活躍維護”的,這也很好。

但你得深挖一下才能判斷是否真有價值。比如“Code Review”這一項寫着 0/10,“最近 22 個 PR 中僅 2 個被審查”,聽起來很糟。他們在幹嘛?我去項目看了下,原來最近一個月裏 22 個 PR 有 20 個是作者自己提的。沒人可以審,所以沒被審這事可能可以接受。分數寫 0,確實拉低了總分,但可能並不重要。再比如其中有一項似乎只是看 README 裏有沒有 OpenSSF 徽章,它就佔了 10% 的分數。這也許不怎麼有用。所以你不能因為 5.9 看起來不高就武斷地下結論,因為這些分項的意義並不等價。

最後我關注可持續性。如果你能識別出關鍵依賴,評估它們的可持續性至關重要。我有一個半小時的專門演講來講這個。我剛看到“還剩 5 分鐘”的提示,就不試圖在剩下時間塞滿整個半小時版本了。基本觀點是:在我看來,開源維護者如果得到支持與幫助,通常會寫出更好的代碼。貢獻不一定非得是錢,儘管我認識的大多數維護者如果開了 GitHub Sponsors 或其他渠道,都會很樂意接受資助。你也可以在文檔、代碼、日常維護上幫忙。但關鍵是:最好的做法是去問他們需要什麼。有些維護者可能不需要任何幫助,這完全合理,但很多人是需要的。知道你能如何幫忙很關鍵。

總結一下,安全供應鏈的代價是持續的警惕。有許多參與者,包括 Rust 基金會、Alpha Omega、OpenSSF 等,都在關注整個生態(不止 Rust,也包括 Python 等),努力讓事情儘可能安全。但到某個節點,你也需要審視自己的依賴,確保自己在引入、使用以及更新依賴時做出好的選擇。希望這次演講至少介紹了一個你可以用上的工具,幫助你在此過程中做出更好的決定。非常感謝你們的時間,也感謝邀請我來這裏。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.