首页/开发/CI/CD 与发布速查/外部贡献者 PR 的安全处理
流水线设计54 条

外部贡献者 PR 的安全处理

来自 fork 的代码默认不可信:不要给它机密与写权限;需要验证时用无密环境,并谨慎处理特殊触发事件。

CI/CD 与发布速查开发更新于 2026-10-13

fork PR 安全处理是什么

外部贡献者从 fork 提交 PR 时,代码默认不可信:处理原则是「不可信代码 + 无敏感凭据 + 只读全局资源」。GitHub 的默认防线是这样设计的:pull_request 事件对 fork 不发放 secrets,GITHUB_TOKEN 只有只读权限。真正的危险在于人为绕过默认:pull_request_target(或 workflow_run)会在带凭据的上下文里运行,如果此时 checkout 了 PR 代码再执行,等于把凭据交给攻击者。GitLab 侧要关注 fork 项目与受保护变量、受保护分支的暴露范围。

基本信息

GitHub 默认pull_request 对 fork 不发放 secrets;GITHUB_TOKEN 只读
高危事件pull_request_target、workflow_run:请求发起方是目标仓库,凭据可用,绝不能执行未审代码
GitLab注意受保护变量与受保护分支对 fork 来源的暴露规则
原则不可信代码 + 无敏感凭据 + 只读全局资源

典型使用场景

  • 开源项目接受外部贡献:外部 PR 只跑构建与测试,不接触任何密钥。
  • 需要自动评论/打标签:用「无密运行 + 受信上下文处理结果」两段式,而不是直接给凭据。
  • 内部跨团队 fork 工作流:同样按不可信处理,避免「内部就安全」的假设。
  • 公共仓库的 Runner 选择:外部 PR 优先用托管 Runner,避免自托管机器被持久化利用。
  • 制品验证:外部 PR 产出的构建结果不进入发布通道,只作参考。

安全与危险写法

安全与危险写法
# 安全:fork PR 只跑不需要密钥的构建与测试
on: pull_request
permissions:
  contents: read

# 危险:pull_request_target 下 checkout PR 代码再执行
on: pull_request_target
# steps: checkout PR head -> run 脚本
# 此时凭据可用, PR 代码可直接读取 = 泄露

事件与凭据对照

事件上下文secrets 是否可用
pull_request(fork)PR 代码,只读令牌否
pull_request(同仓分支)正常仓库上下文是
pull_request_target目标仓库,含写权限是(不得执行 PR 代码)
workflow_run上游运行完成后是(只处理产物)
把敏感凭据暴露给 PR 触发的构建等同于向外部公开:审查触发事件与变量范围,再谈功能。
需要对外部 PR 做评论、打标时:让无密作业产出结果,由 workflow_run 等受信上下文消费,切勿翻转依赖关系。

易错点

  • 为省事发全部凭据给 PR 构建:用「需要密钥的步骤只在受信分支执行」替代。
  • 第三方 action 按分支引用(@main):内容可变,换成固定版本或提交摘要。
  • pull_request_target 里 checkout PR head 后执行脚本:凭据直接暴露给外部代码,这是最常被利用的组合。
  • 公共仓库用自托管 Runner 跑外部 PR:机器可被持久化利用,优先托管 Runner 或一次性隔离环境。
  • 把 PR 可控内容直接拼进脚本(分支名等):存在命令注入面,改为经环境变量传值再引用。

数据来源与参考

  • GitHub Docs《Security hardening for GitHub Actions》
  • GitHub Docs《Events that trigger workflows》
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌