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 文件,按这个原则过一遍:
- CI job(跑在 PR 上的):加
cache-mode: read,享受缓存加速,但不产出污染。 - 发版 job(跑在 push 上的):保持默认 write,或者如果你有专职的构建缓存 job,考虑拆成
write-only+ 另一个read。 - 纯脚本 job:从不用缓存的,加
cache-mode: none,彻底免开这个攻击面。
这个改动不破坏任何现有 workflow——你没配 cache-mode,GitHub 继续用现有安全默认值。但你现在有了选择权。
供应链安全这件事,有时候就是多一行配置的事。
评论区
登录后可评论。