編輯評論

在軟體供應鏈攻擊頻繁的當下,Astral 這篇文章的價值在於它提供了一份可操作的安全實踐清單,而非空泛的原則。許多開源專案維護者知道「應該」注意安全,但往往不確定從何做起。

特別值得關注的是 Astral 對 GitHub Actions 的防禦性使用策略。他們直接禁用了 pull_request_targetworkflow_run 等高風險觸發器,這個決策雖然會限制某些功能,但大幅降低了被攻擊的表面積。這種「寧可放棄便利性也要保證安全性」的思維,正是許多專案缺乏的。

另一個亮點是他們對 Trusted Publishing 的積極採用。這項技術透過 OIDC 取代長期憑證,從根本上消除了憑證洩漏這類最常見的攻擊向量。目前業界對 Trusted Publishing 的採用率仍然偏低,Astral 的實踐可以作為其他專案的參考範例。

不過,Astral 也坦言某些安全措施(如自行開發 GitHub App)對小型專案或個人維護者來說門檻過高。這反映了開源安全領域的一個核心矛盾:最佳安全實踐往往需要資源支持。未來或許需要更多平台層級的改進,或是大型專案帶頭分享可重用的安全基礎設施。

結論摘要

  • GitHub Actions 的預設安全設定不佳,應禁用 pull_request_target 等高風險觸發器,並將所有 actions 固定到完整 commit SHA
  • 使用 Trusted Publishing 取代長期憑證,結合 deployment environments 和雙人審批機制,可大幅降低發布流程的風險
  • 透過 Sigstore attestations 和 immutable releases 建立加密驗證鏈,讓用戶能驗證版本來自正當的發布流程
  • 依賴管理應採用 cooldown 機制,避免在新版本發布後立即更新,同時與上游專案保持聯繫並貢獻安全修復
  • 對於無法在 CI/CD 中安全執行的操作(如對第三方 PR 留言),應使用 GitHub App 隔離執行

原文翻譯

Astral 打造了全球數百萬開發者依賴並信任的工具。

這份信任包含對我們安全防護的信心:開發者合理地期望我們的工具(以及建置、測試和發布這些工具的流程)是安全的。供應鏈攻擊的興起,以最近的 TrivyLiteLLM 黑客事件為典型代表,讓開發者開始質疑他們能否信任自己的工具。

為此,我們想分享一些用於保護工具的技術,希望對以下群體有所幫助:

  1. 我們的用戶,讓他們了解我們如何保護他們的系統安全;
  2. 其他維護者、專案和公司,可能從我們使用的某些技術中受益;
  3. CI/CD 系統的開發者,使專案不需要遵循非直觀的路徑或避免有用的功能來維持安全且穩健的流程。

CI/CD 安全

我們透過在 GitHub Actions 上運行的廣泛 CI/CD 工作流程,維持 Ruffuvty 的開發速度。沒有這些工作流程,我們將難以以我們要求的速度和信心程度來審查、測試和發布我們的工具。我們的 CI/CD 工作流程也是我們安全防護的關鍵部分,因為它們讓我們能夠將關鍵的開發和發布流程遠離本地開發機器,並保持在可控、可觀察的環境中。

GitHub Actions 對我們來說是合乎邏輯的選擇,因為它與 GitHub 有緊密的原生整合,加上對 contributor workflows 的成熟支援:任何想要貢獻的人都可以使用我們自己使用的相同流程來驗證他們的 pull request 是否正確。

不幸的是,這也有反面:GitHub Actions 的預設安全性不佳,而像 Ultralyticstj-actionsNx 這樣的安全漏洞都始於像 pwn requests 這樣常見的弱點。

以下是我們為保護 CI/CD 流程所做的一些事情:

  • 我們禁止了 GitHub 許多最危險和不安全的觸發器,例如 pull_request_targetworkflow_run,涵蓋整個 GitHub 組織。這些觸發器幾乎不可能安全使用,攻擊者不斷找到濫用它們的方法,所以我們根本不允許使用。

我們對這些觸發器的經驗是,許多專案_認為_他們需要這些觸發器,但絕大多數使用情況最好用權限較低的觸發器(如 pull_request)替換,或完全移除。例如,許多專案使用 pull_request_target 讓第三方貢獻者觸發的工作流程能夠在 PR 上留下評論,但這些用例通常可以透過 job summaries 甚至只是將相關資訊留在工作流程的日誌中來滿足。

當然,有一些用例確實需要這些觸發器,例如任何_確實_需要在第三方 issue 或 pull request 上留下評論的情況。在這些情況下,我們建議完全離開 GitHub Actions,改用監聽相關事件並在獨立上下文中執行的 GitHub App(或 webhook)。我們在下面的 Automations 章節中更詳細地討論了這個模式。

  • 我們要求所有 actions 都必須固定到特定的 commit(而不是標籤或分支,它們是可變的)。此外,我們交叉檢查這些 commit,以確保它們與實際的發布版本狀態匹配,而不是 impostor commits

我們透過兩種方式做到這一點:首先使用 zizmor 的 unpinned-usesimpostor-commit 審計,然後再次使用 GitHub 自己的「要求 actions 固定到完整長度 commit SHA」政策。前者讓我們能夠快速本地檢查(並防止 impostor commits),後者則是工作流程執行的硬性門檻,實際上確保_所有_ actions(包括嵌套 actions)都完全 hash-pinned。

啟用後者是一項非平凡的艱鉅任務,因為它要求_間接_ action 使用(我們呼叫的 actions 所呼叫的 actions)也必須 hash-pinned。為了實現這一點,我們與我們的下遊協調(範例),在我們的整個依賴圖中落地 hash-pinning。

這些檢查共同增加了我們對工作流程的_可重現性_和_封閉性_的信心,這反過來又增加了我們對它們安全性的信心(在攻擊者能夠破壞依賴 action 的能力存在下)。

然而,雖然_必要_,但這並不_充分_:hash-pinning 確保_action 的_內容是不可變的,但不能防止這些不可變的內容做出可變的決定(例如從 GitHub repository 的 releases 安裝最新版本的二進位檔)。GitHub 和第三方工具在檢測這類不可變性差距方面表現不佳,所以我們目前依賴對 action 依賴項的手動審查來檢測這類風險。

當手動審查_確實_發現差距時,我們與我們的上游合作來彌補它們。例如,對於內部使用原生二進位檔的 actions,這是透過在二進位檔的下載 URL 和加密雜湊之間嵌入映射來實現的。這個雜湊隨後成為 action 不可變狀態的一部分。雖然這不能確保二進位檔_本身_是真實的,但它確保攻擊者無法有效地篡改二進位檔的可變指標(例如不可變的標籤或發布)。

  • 我們在多個地方限制我們的工作流程和工作權限:我們在組織層級預設為唯讀權限,此外,我們還以 permissions: {} 開始每個工作流程,並只在逐個工作的基礎上擴大權限。

  • 我們盡可能隔離我們的 GitHub Actions secrets:我們不使用組織或 repository 層級的 secrets,而是使用 deployment environments 和環境特定的 secrets。這讓我們能夠進一步限制潛在漏洞的影響範圍,因為被破壞的測試或 linting 工作將無法訪問例如發布發布版本所需的 secrets。

為了做到這些,我們利用 GitHub 自己的設定,以及像 zizmor(用於靜態分析)和 pinact(用於自動固定)這樣的工具。

Repository 和組織安全

除了我們的 CI/CD 流程外,我們還採取了一系列措施來限制 Astral 組織內帳戶和 repository 漏洞的可能性和影響:

  • 我們限制具有 admin 和其他高權限角色的帳戶數量,大多數組織成員只對他們需要工作的 repository 具有讀寫訪問權限。這減少了攻擊者可以破壞以獲得我們組織層級控制訪問權限的帳戶數量。

  • 我們對 Astral 組織的所有成員執行_強_2FA 方法,超出 GitHub 要求_任何_ 2FA 方法的預設。實際上,這要求所有 Astral 組織成員都具有不弱於 TOTP 的 2FA 方法。如果 GitHub 允許我們僅執行抗釣魚的 2FA 方法(例如僅 WebAuthn 和 Passkeys),我們將這樣做。

  • 我們在組織範圍內實施分支保護規則:不能強制推送到 main,並且必須始終透過 pull request 進行。我們還禁止創建特定分支模式(如 advisory-*internal-*)以防止安全工作的過早披露。

  • 我們實施標籤保護規則,防止在 release deployment 成功之前創建發布標籤,發布部署本身需要至少一名其他團隊成員的手動批准。我們還防止更新或刪除標籤,使它們在創建後實際上不可變。除此之外,我們還加了一層分支限制:發布部署只能針對 main 創建,防止攻擊者使用不相關的第一方分支嘗試繞過我們的控制。

  • 最後,我們禁止 repository admins 繞過上述所有保護。我們所有的保護都在組織層級執行,這意味著攻擊者即使破壞了具有特定 repository admin 訪問權限的帳戶,也無法禁用我們的控制。

為了幫助其他人實施這類分支和標籤控制,我們分享了一個 gist,顯示我們使用的一些 rulesets。這些 rulesets 特定於我們的 GitHub 組織和 repository,但您可以將它們作為自己政策的起點!

Automations

有些事情是 GitHub Actions 可以做到的,但_不能_安全地做到,例如在第三方 issue 和 pull request 上留下評論。大多數時候最好只是放棄這些功能,但在某些情況下它們是我們工作流程的寶貴部分。

在後者這些情況下,我們使用 astral-sh-bot 在 GitHub Actions 之外安全地隔離這些任務:GitHub 發送給我們與 GitHub Actions 將接收的相同事件數據(因為 GitHub Actions 消費與 GitHub Apps 相同的 webhook 負載),但具有更多控制力和更少的隱式狀態。

然而,GitHub Apps 仍有一個問題:應用程序不_消除_操作所需的任何敏感憑證,它只是將它們移到一個不像 GitHub Actions 那樣普遍混合代碼和數據的環境中。例如,應用程序不會像工作流程那樣容易受到 template injection 攻擊,但仍可能包含 SQLi、prompt injection 或其他允許攻擊者濫用應用程序憑證的弱點。因此,用與任何其他軟體開發相同的安全思維對待 GitHub App 開發是_至關重要的_。這也延伸到不受信任的代碼:使用 GitHub App 並_不_使運行不受信任的代碼變得安全,它只是使意外執行變得更加困難。如果您的工作流程_需要_運行不受信任的代碼,它們_必須_使用 pull_request 或其他「安全」觸發器,不會向第三方 pull request 提供任何特權憑證。

儘管如此,我們發現 GitHub App 模式對我們很有效,我們向具有類似需求的其他維護者和專案推薦它。它的主要缺點在於複雜性:它需要開發和託管 GitHub App,而不是編寫 GitHub 為您編排的工作流程。我們發現像 Gidgethub 這樣的框架使 GitHub Apps 的開發過程相對簡單,但託管仍然是時間和成本的負擔。

一人和小型愛好者開源專案仍然沒有很好的 GitHub App 選項,這是一個不幸的現實;我們的希望是,公司和擁有彌補 GitHub Actions 作為平台缺陷所需資源的較大專案可以主導這個領域的可用性增強。

我們推薦 Mariatta 的這個教程作為在 Python 中構建 GitHub Apps 的良好介紹。我們還計劃在未來開源 astral-sh-bot

Release 安全

到目前為止,我們涵蓋了與 GitHub 緊密相關的方面,作為 Astral 工具的源主機。但我們的許多用戶透過其他機制安裝我們的工具,例如 PyPIHomebrew 和我們的 Docker 映像。這些分發渠道為隱喻的供應鏈增加了另一個「環節」,需要單獨考慮:

  • 盡可能,我們使用 Trusted Publishing 發布到 registry(如 PyPI、crates.ioNPM)。這種技術消除了對長期 registry 憑證的需求,進而緩解了包裹接管最常見的來源之一(CI/CD 平台中的憑證洩漏)。

  • 盡可能(目前是我們的二進位和 Docker 映像發布),我們生成基於 Sigstore 的 attestations。這些 attestations 在發布的產品和生成它的工作流程之間建立加密可驗證的連結,進而允許用戶驗證他們的 uv、Ruff 或 ty 構建來自我們的實際發布流程。您可以查看我們最近的 uv attestations作為此範例。

  • 我們使用 GitHub 的 immutable releases 功能來防止我們在 GitHub 上發布的構建的事後修改。這解決了一種常見的攻擊者轉向技術,其中先前發布的構建被惡意構建替換。這種技術的一種變體在最近的 Trivy 攻擊中被使用,攻擊者強制推送覆蓋先前的標籤以引入 trivy-actionsetup-trivy actions 的被破壞版本。

  • 我們不使用快取來改善發布期間的構建時間,以防止攻擊者透過 GitHub Actions cache poisoning attack 破壞我們的構建。

  • 為了降低攻擊者發布我們工具的_新_惡意版本的風險,我們在發布流程中使用了一堆保護:

    • 我們的發布流程在專用的 GitHub deployment environment 中隔離。這意味著不在發布環境中運行的工作(如測試和 linters)無法訪問我們的發布 secrets。

    • 為了_啟動_發布環境,啟動工作必須至少由 Astral 組織的另一名特權成員批准。這緩解了單個流氓或被破壞的帳戶能夠發布惡意發布(或滲漏發布 secrets)的風險;攻擊者需要破壞至少兩個不同的帳戶,兩者都具有強 2FA。

在我們擁有大量發布工作的 repository(如 uv)中,我們使用不同的 release-gate 環境來應對 GitHub 觸發使用發布環境的_每個_工作的批准的事實。這保留了雙人批准要求,有一個額外的跳躍:一個小的、最小權限的 GitHub App透過 deployment protection rulerelease-gaterelease 調解批准。

* 最後,我們使用標籤保護 [ruleset](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets) 來防止在發布部署成功之前創建發布的標籤。這防止攻擊者繞過正常發布流程直接創建標籤和發布。
  • 對於透過我們的 standalone installer 安裝 uv 的用戶,我們透過直接嵌入到安裝程式源代碼中的校驗和來強制執行已安裝二進位檔的完整性。

我們的發布流程還涉及「連鎖」變更,例如更新我們的公開文檔、版本清單和官方pre-commit hooks。這些是我們透過專用的 bot 帳戶和透過這些帳戶發布的細粒度 PATs 來保護的特權操作。

未來,我們還正在研究在 macOS 和 Windows 上使用官方開發者憑證進行 codesigning。

依賴安全

最後但同樣重要的是依賴問題。與幾乎所有現代軟體一樣,我們的工具依賴第三方依賴(直接和傳遞)生態系統,每個依賴都處於隱含的信任位置。以下是我們用來衡量和緩解上游風險的一些措施:

  • 我們使用像 DependabotRenovate 這樣的依賴管理工具來保持我們的依賴更新,_並_在它們包含已知漏洞時通知我們。

  • 總體而言,我們與上述結合使用 cooldowns,以避免在新版本發布後立即更新依賴,因為這是暫時被破壞的依賴最可能影響我們的時候。

Dependabot 和 Renovate 都支援 cooldowns,並且 uv 也有內建支援。我們發現 Renovate 能夠在每組基礎上配置 cooldowns 特別有用,因為它允許我們放鬆我們自己(第一方)依賴的 cooldown 要求,同時對大多數第三方依賴保持它。

  • 我們與許多上游依賴維護社交聯繫,並與他們一起進行常規和安全貢獻(包括修復他們自己的 CI/CD 和發布流程)。例如,這是我們最近對 apache/opendal-reqsign 的一項貢獻,幫助他們收緊 CI/CD 安全。

  • 此外,我們與生態系統中的_相鄰_專案和工作組維護社交聯繫,包括 Python Packaging AuthorityPython Security Response Team。這些聯繫在分享資訊方面證明是非常寶貴的,例如當針對 pip 的報告也影響 uv(反之亦然),或者當 CPython 的安全發布需要發布 python-build-standalone 時。

  • 我們對添加_新_依賴持保守態度,並且在實際情況下且對用戶干擾最小的情況下,我們尋求_消除_依賴。在未來的發布週期中,我們希望刪除一些與支援罕見壓縮方案相關的依賴,作為與 Python 包裝標準保持一致的更大努力的一部分。

  • 更一般地說,我們對我們的依賴帶來的_東西_持保守態度:我們試圖避免引入 binary blobs 的依賴,並仔細審查我們依賴的功能以禁用我們不需要或不需要的功能。

  • 最後,我們以財務方式(透過我們的 OSS Fund)為我們依賴的專案或推動整個 OSS 生態系統向前發展的專案的可持续性做出貢獻。

結語

開源安全是一個困難的問題,部分是因為它實際上是許多問題(有些是技術性的,有些是社會性的)偽裝成一個問題。我們涵蓋了許多用於解決這個問題的技術,但這篇文章絕不是詳盡的清單。它也不是一個_靜態_清單:攻擊者是安全過程中的動態參與者,防禦措施必然根據他們不斷變化的技術而演變。

考慮到這一點,我們想回顧上述一些值得最多關注的點:

  • 尊重 CI/CD 的限制:在 CI/CD 中做所有事情非常誘人,但有些事情是 CI/CD(特別是 GitHub Actions)無法安全做到的。對於這些事情,通常最好完全放棄它們,或使用 GitHub App 或類似工具在 CI/CD 之外隔離它們。

儘管如此,重要的是不要_矯枉過正_並完全放棄 CI/CD:如上所述,CI/CD 是我們安全防護的關鍵部分,可能也是您的!保護 GitHub Actions 如此困難是不幸的,但我們認為這是值得的,相對於完全不使用託管 CI/CD 所帶來的速度和安全風險。

特別,我們強烈建議使用 CI/CD 進行發布流程,而不是依賴本地開發機器,_特別是_當這些發布流程可以透過抗濫用和披露的憑證方案(如 Trusted Publishing)保護時。

  • 隔離和消除長期憑證:漏洞後傳播的最常見形式是濫用長期憑證。盡可能完全消除這些憑證(例如,透過 Trusted Publishing 或其他基於 OIDC 的認證機制)。

當消除不可能時,_隔離_這些憑證到最小可能範圍:將它們放在具有額外啟動要求的特定部署環境中,並僅發布具有完成給定任務所需最小必要權限的憑證。

  • 加強發布流程:如果您在 GitHub 上,使用 deployment environments、批准、標籤和分支 rulesets 以及 immutable releases 來減少攻擊者在帳戶接管或 repository 漏洞事件中的自由度。

  • 保持對依賴的關注:保持對依賴樹整體健康的關注對於了解您自己的風險配置至關重要。使用工具和人工勞動力來保持您的依賴安全,_並_幫助它們保持自己的流程和依賴安全。

最後,我們仍在評估上述許多技術,並且幾乎肯定會在未來幾週和幾個月內調整(並加強)它們,因為我們更多地了解它們的限制以及它們如何與我們的開發流程互動。也就是說,這篇文章代表了一個時間點,而不是我們對開源工具安全的思考的最終定論。