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 等受信上下文消费,切勿翻转依赖关系。