workflow 默认有缓存读写权限,但你的 CI 可能根本不需要——今天 GitHub 用四个等级把这个风险彻底拆了

你写了一个 GitHub Actions workflow,跑起来发现缓存没生效,你以为是因为它默认就不开缓存。

其实恰恰相反——GitHub Actions 的缓放在默认分支上,所有 workflow 共享。你那个跑在 pull_request_target 上的自动化 workflow,有权读写整个仓库的缓存。只是你不知道,也从来没被告知。

这个默认设置惹过事。攻击者利用低权限 workflow(比如 PR 里注入的脚本)向缓存写入恶意内容,等高权限 workflow(比如定时发版的 job)恢复并执行它——这就是缓存投毒(cache poisoning)。安全研究界多年前就演示过这个攻击路径,LinkedIn 上也报道过真实事件。

从 2026 年 9 月 10 日起,GitHub 给每个 workflow 和 job 加了一个 cache-mode 字段,你终于能说「这个 job 只能读缓存,不准写」。

四个等级,粒度到 job:

  • read:允许恢复缓存,禁止写入。低信任事件(如 pull_request_target)默认就是这个。
  • write:允许恢复和写入。可信事件(如 push)默认就是这个。
  • write-only:只允许写入,禁止恢复。适合专职写缓存的 job。
  • none:禁止所有缓存访问。

job 级设置优先于 workflow 级设置。这个模式由缓存服务强制执行,穿透 reusable workflow——被调用方拿到的权限不会比调用方给的更多。

一个值得注意的细节: 如果你给 pull_request_target 显式声明了 write 或 write-only,GitHub Actions 会在 workflow 上加一个警告 annotation,提示你增加了缓存投毒风险。安全默认值不是摆设。

下一步是什么?

去翻一下你的 workflow 文件,按这个原则过一遍:

  1. CI job(跑在 PR 上的):加 cache-mode: read,享受缓存加速,但不产出污染。
  2. 发版 job(跑在 push 上的):保持默认 write,或者如果你有专职的构建缓存 job,考虑拆成 write-only + 另一个 read
  3. 纯脚本 job:从不用缓存的,加 cache-mode: none,彻底免开这个攻击面。

这个改动不破坏任何现有 workflow——你没配 cache-mode,GitHub 继续用现有安全默认值。但你现在有了选择权。

供应链安全这件事,有时候就是多一行配置的事。

评论区

0 条评论

登录后可评论。