紀錄工作經驗、相關知識,解決技術相關問題。

時事新聞, 資訊相關

GitHub Actions 鎖定版本:用完整 commit SHA 與 lockfile 防供應鏈攻擊

GitHub Actions 鎖定版本:用完整 commit SHA 與 lockfile 防供應鏈攻擊 特色圖片

近年開源套件與 CI 工具的供應鏈事件不少,2025 年 3 月的 tj-actions/changed-files(CVE-2025-30066)就是其中一起:一個很多專案都在用的 GitHub Action 被植入惡意程式碼,可能外洩 workflow 裡的 secrets。

最近開發圈又有人整理了一輪「怎麼鎖住 GitHub Actions 的版本」,而且 GitHub 在 2026 年 10 月底把 workflow lockfiles 推到正式上線,官方建議的做法也跟著變了。

這篇整理 GitHub Actions 鎖定版本的做法:為什麼只寫 @v4 不夠安全、用完整 commit SHA 怎麼寫、新的 lockfile 又是什麼,以及鎖了之後怎麼更新、組織怎麼強制。

為什麼要鎖 GitHub Actions 版本

workflow 裡的 uses: owner/repo@v4,指的是那個 Action 儲存庫裡名為 v4 的 tag。

GitHub 官方文件(Secure use reference,Using third-party actions 一節)提醒:如果攻擊者取得該 Action 儲存庫的存取權,tag 可以被移動或刪除。你的 workflow 不用改一個字,下次執行時拿到的就可能是別的程式碼。

一排貼著紙標籤的箱子,其中一張標籤被一隻手從原本的箱子撕下,貼到另一個箱子上
圖:同一個標籤,下次可能指向別的內容

CISA 對 tj-actions/changed-files 的通報指出,受影響的時間區間是 2025 年 3 月 12 日 00:00 UTC 到 3 月 15 日 12:00 UTC,可能暴露的內容包括存取金鑰、GitHub Personal Access Token、npm token 與 RSA 私鑰。

GitHub Advisory(GHSA-mrrh-fwg8-r2c3)則說明,攻擊者回頭修改了多個版本 tag,讓它們指向惡意 commit;這正是只用 tag 引用的風險。

官方現在給的順序是:

做法官方文件的定位
用 lockfile 鎖定 workflow 依賴「最佳措施」,採用後取代 SHA 固定
固定到完整 commit SHA無法使用 lockfile 時的做法
固定到 tag只在信任該 Action 的建立者時才這樣做

不論用哪一種,官方文件也一再要求:第三方 Action 的原始碼要自己審過。鎖版本只能防「之後被偷換」,不能證明你當下鎖的那一版是安全的。

用 commit SHA 固定的寫法

固定到 commit SHA 的意思,是把 @ 後面的 tag 換成 40 個字元的完整 commit SHA。官方文件說明這樣可以降低 Action 儲存庫被植入後門的風險,因為攻擊者得產生 SHA-1 碰撞,且對上有效的 Git 物件。

steps:
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

幾個要注意的地方:

SHA 要來自 Action 本身的儲存庫

官方文件提醒,選 SHA 時要確認它屬於原始 Action 儲存庫,而不是某個 fork。

後面留一段版本註解

SHA 本身人看不懂。習慣上在行尾用 # 註明對應的版本,之後 review 才知道現在用哪一版。

必須是完整 SHA

GitHub 官方 changelog 說明,強制 SHA 固定的政策檢查的是完整 commit SHA;另外 GitHub 早在 2021 年的 changelog「GitHub Actions: Short SHA deprecation」就宣布 Actions 不再支援以縮短的 commit SHA 引用,所以不要為了好看截短。

新做法:workflow lockfiles

GitHub 在 2026 年 10 月 28 日宣布 workflow lockfiles 正式上線(GA),適用 Enterprise Cloud、Team、Free 等方案。

它的概念是:workflow 裡仍然寫容易閱讀的 tag 或分支名稱,實際執行的 commit 則記錄在 .github/workflows/actions.lock,連 Action 的間接依賴(transitive dependencies)也一起鎖。即使之後 tag 被移動,跑的還是 lockfile 裡記的那個 commit。

檢查員拿著清單,逐一核對輸送帶上貼有封條的箱子,每個封條都對得上清單上的一行
圖:清單記錄實際執行的那一版

產生方式是用 GitHub CLI 的 gh-actions-lock 擴充套件:

  1. 安裝擴充套件。
    gh extension install github/gh-actions-lock
  2. 在儲存庫根目錄產生或更新 lockfile。
    gh actions-lock
  3. 把修改後的 workflow 和 .github/workflows/actions.lock 一起 commit。

在 CI 裡可以加一道檢查,lockfile 過期時就失敗:

gh actions-lock --verify

寫入 lockfile 需要 workflow OAuth scope。另外官方建議把 .github/workflows/actions.lock 加進 CODEOWNERS,並用 branch protection 或 ruleset 要求 code owner 核准,這樣 lockfile 裡 SHA 的變動才會有人看。

鎖了之後怎麼更新:Dependabot

鎖死版本的代價,是不會自動拿到新版與修補。解法是讓 Dependabot 定期開 PR。

.github/dependabot.yml 的寫法如下(取自官方文件):

version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

官方文件指出 directory 必須設成 "/",Dependabot 才會去找 .github/workflows 底下的 workflow 檔。

兩種做法在 Dependabot 上有些差異:

項目SHA 固定lockfile
版本更新 PRDependabot 會更新 owner/repo@<commit> 形式的參照升級 Action 版本時,同一個 PR 內一併更新 lockfile 對應項目,不需額外設定
弱點警示官方文件說明,Dependabot 不會對固定到 SHA 的 Action 建立 alert官方文件未針對這點另外說明
例外本地 Action 與本地 reusable workflow 會被忽略Dependabot 不會把沒有 lockfile 項目的 workflow 納入,只維護、不擴大

換句話說,用 SHA 固定的話,Dependabot 的弱點警示會失靈,得靠定期更新來補;官方文件也提到,若 commit 沒有對應任何 tag,Dependabot 會更新到最新 commit,不一定是最新 release。

至於原作者提醒要搭配的「依賴冷卻期(dependency cooldowns)」,也就是新版本放一段時間才升級,細節請看他的文章與各工具的文件。

組織與 Repo 層級的強制設定

只靠大家自覺很難全面,GitHub 提供兩種可以強制的政策。

要求固定完整 commit SHA

官方 changelog 在 2025 年 8 月 15 日宣布,allowed actions 政策新增「強制 SHA 固定」的選項,企業、組織、儲存庫三個層級都有。啟用後,使用沒有固定 SHA 的 Action,workflow 會直接失敗。

Repo 層級的設定路徑:

  1. 進入儲存庫的 Settings。
  2. 左側選 Actions,再選 General。
  3. 在 Actions permissions 下勾選「Require actions to be pinned to a full-length commit SHA」,按 Save。

組織層級同樣是 Settings > Actions > General,在 Policies 選一個選項後勾選同一個核取方塊。官方文件註明 reusable workflows 仍可用 tag 引用,若上層企業政策已覆寫,下層也可能改不了。

要求 lockfile(Require lockfile)

在組織或 Repo 的 Settings > Actions > Policies 建立新政策,可以啟用「Require lockfile」。啟用後,沒有出現在儲存庫 lockfile 裡的目標 workflow 會被擋下,連沒有 uses: 依賴的 workflow 也一樣。

官方建議的上線順序:

  1. 先在 Evaluate 模式試跑(僅限 GitHub Enterprise Cloud),搭配 Policy insights 看影響範圍。
  2. 逐步把 workflow 與儲存庫納入。
  3. 確認每個目標 workflow 都有被涵蓋之後,再切到 Active。

另外,被其他儲存庫呼叫的 reusable workflows 需要有自己的 lockfile,呼叫端的 lockfile 不會涵蓋它。官方也說明這個政策可以和「要求固定完整 commit SHA」同時啟用。

參考資料與延伸閱讀

發佈留言