1128 字
6 分鐘

GitHub Actions cache-mode 怎麼設才安全?restore-keys 仍不是可信來源

GitHub Actions 的 cache 很適合存 package manager 已下載的依賴;它不適合存 token、.env、部署憑證或任何一旦被讀到就有風險的內容。原因不是 cache 一定會外洩,而是能還原 cache 的 workflow 必須把它當成外部輸入,而不是你剛剛在同一個 job 寫入的私有檔案。

直接答案是:cache 只存可重新下載或重新產生的資料;低信任事件通常使用 cache-mode: read,trusted push 才負責寫入;restore-keys 只當效能 fallback,不能成為信任邊界。

先理解 cache-mode 的四種權限#

cache-mode 把 restore 和 save 拆成四種可 review 的模式:

mode可以 restore可以 save適合情境
read可以不可以fork PR、外部貢獻或只需加速安裝
write可以可以trusted push CI,既還原又更新快取
write-only不可以可以只建立新快取,不使用既有內容
none不可以不可以明確不讓該 workflow 接觸 Actions cache

未設定時,GitHub 會依事件信任層級選預設:低信任事件傾向 read,trusted event 才能使用 write。既有 workflow 可以保留這個安全預設,但仍要搜尋 repository 內是否有明確覆寫。

workflow-level 可以先設整體基線,job-level 再縮小單一工作:

name: test
on:
pull_request:
push:
cache-mode: read
jobs:
test:
cache-mode: read
runs-on: ubuntu-latest
steps:
- run: pnpm install --frozen-lockfile
- run: pnpm test

job 的設定優先於 workflow。若 job 呼叫 reusable workflow,caller 也能把被呼叫工作流限制為 read 或 none,避免被呼叫端把 cache 權限向上放大。

先理解命中順序#

actions/cache 先找完整的 key,再找 key 的部分匹配;若仍找不到,才依序搜尋 restore-keys。部分匹配有多筆時,會使用最近建立的一筆。

- uses: actions/cache@v4
with:
path: ~/.cache/pnpm
key: pnpm-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
pnpm-${{ runner.os }}-

這段設定的目標是「lockfile 不變時拿到精確 cache;變了時可暫時還原同平台的較舊依賴下載快取」。它不是「永遠拿到正確的 node_modules」。安裝步驟仍要根據 lockfile 驗證依賴,例如使用你專案既有的 frozen lockfile 安裝方式。

不要把 cache 當成 artifact 或 secret store#

cache 與 artifact 的使用目的不同:cache 用於可重建、但重新取得成本高的資料;artifact 則是用於保存 workflow 產物、供後續 job 或人工查看。兩者都不適合裝 secrets。

GitHub 文件也明確警告:能讀取 cache 的人可能透過 pull request 存取其內容。因此以下資料不應放進 cache path:

  • .npmrc、.pypirc、.netrc 或含 token 的設定檔。
  • .env、雲端憑證、SSH key 與暫存 deployment credential。
  • 有可能在下一個高權限 job 被直接執行的產物。

即使低信任 workflow 對 default branch cache 是 read-only,還原後的檔案仍應當成不可信資料。快取隔離能降低寫入風險,不會把快取內容變成可直接執行的安全輸入。

低信任事件採 restore-only 更清楚#

fork PR 或其他低信任觸發來源如果只需要加速安裝,使用 restore-only 操作能把意圖寫明:

- name: Restore dependency cache
uses: actions/cache/restore@v4
with:
path: ~/.cache/pnpm
key: pnpm-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
pnpm-${{ runner.os }}-

這避免 job 嘗試儲存但因權限被拒時留下令人誤解的 warning。真正負責更新 cache 的 trusted workflow,則可以放在預設分支的 push CI。

若低信任 workflow 明確指定 write 或 write-only,GitHub 會以 warning 提醒可能重新打開 cache poisoning 風險。除非你能證明輸入、權限、執行環境與下游使用者都在同一個信任邊界,否則不要為了 cache hit 改掉安全預設。

當 cache operation 因 mode 被跳過時,GitHub 會留下資訊訊息並繼續 workflow;不要把「沒有 save」誤判成 failure,也不要把 job 綠燈當成 cache 內容已驗證。

快取設定檢查表#

  1. path 只包含 package manager cache,沒有 secrets 或 build deploy output。
  2. 完整 key 包含 OS 與 lockfile hash;有 matrix 時也加入會改變二進位相容性的版本。
  3. restore key 由最具體到最寬鬆排序,並接受它只改善速度、不保證內容正確。
  4. 還原後仍執行依賴安裝與測試;不要跳過 integrity 驗證。
  5. review workflow-level、job-level、reusable workflow caller 與 ACTIONS_CACHE_MODE 的最終值。
  6. 不可信 PR 與有 secrets 的寫入/部署 workflow 分離。

快取風險和 pull_request_target 的風險都來自信任邊界被混在同一個工作流程;若 workflow 還會 checkout PR 程式碼,請再看 GitHub Actions 的 fork PR checkout 保護。

參考資料:

GitHub Changelog:Control GitHub Actions cache access with cache-mode

GitHub Docs:Workflow syntax

GitHub Docs:Dependency caching reference

GitHub Actions cache-mode 怎麼設才安全?restore-keys 仍不是可信來源
https://laplusda.com/posts/github-actions-cache-security-restore-keys/
作者
Zero
發佈於
2026-07-29
許可協議
CC BY-NC-SA 4.0