GitHub Actions 固定到 SHA:一条能落地的供应链纪律

版本标签是可以移动的

工作流里常见的写法是 uses: actions/checkout@v4。问题是 v4 只是一个可以随时被重新指向的标签——仓库作者(或拿到仓库权限的人)推一个新提交、把标签挪过去,你下次运行拉到的就是另一份代码。

这不是理论风险。第三方 action 拿到 GITHUB_TOKEN 之后,在权限放开的情况下能改 release、能推代码。一个被接管的 action,等于你主动把 CI 的钥匙递过去。

固定到完整 commit SHA 是唯一能锁住内容的方式:

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

光锁 SHA 不够,还要限权

仓库设置决定了 GITHUB_TOKEN 的默认权限。新仓库默认只读,但老仓库、以及为了跑通流水线而手动放开过权限的项目,往往还停在可写状态。别赌默认值,在工作流顶层显式收紧:

permissions:
  contents: read

需要写的时候,只在具体 job 上开对应权限。这样即使某个 action 被投毒,它能造成的破坏也被限制在「读代码」这个级别。

最危险的两个触发器

pull_request_target 会在目标仓库的上下文里运行,并且能拿到 secrets。如果它又 checkout 了 PR 里的代码,等于让外部贡献者在你的凭据环境里执行任意脚本。这个组合出过不止一次真实事故。

另一个是 workflow_run,它能在有 secrets 的环境里运行,但触发源可能是 fork 的 PR。用之前先想清楚:这段代码是谁写的。

密钥:能不用长期凭据就不用

云厂商的 access key 放在 secrets 里,一旦工作流被攻破就直接泄露。更好的做法是 OIDC 联合身份:工作流向云厂商换取短期凭据,不落盘、不过期可用。

AWS、GCP、Azure 都支持,配置比想象中简单,代价是要多写几行信任策略。

让依赖自己更新

SHA 锁死之后,升级成了手工活。用 Dependabot 的 github-actions 生态可以自动提 PR,把 SHA 和注释一起更新:

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

这样你既能锁内容,又不会锁死版本。

一个检查清单

  • 所有 uses: 都是 40 位 SHA,后面跟版本注释;
  • 顶层 permissions 默认只读;
  • 没有 pull_request_target + checkout PR 代码的组合;
  • 云凭据优先 OIDC,长期 key 有轮换计划;
  • 第三方 action 数量控制在个位数,每个都看过源码或至少看过发布记录。

我的判断

CI 是现代软件里权限最集中的地方之一:它能读代码、能发布、能部署、能拿密钥。偏偏它的配置往往是最随意的那部分——复制粘贴一段 YAML,跑通就不再看。

固定 SHA 和最小权限,成本是每次升级多花两分钟,收益是把「一个第三方仓库被接管」从「全线失守」降级成「一次失败的构建」。

延伸阅读

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发
头像/SUENCA
欢迎您留下宝贵的见解!
提交
头像/SUENCA

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容