Skip to main content

安全地使用 pull_request_target

了解此 pull_request_target event项的安全风险。

本指南可帮助你评估工作流是否应使用 pull_request_target 事件并了解所涉及的安全风险。 它还介绍了保护 GitHub 适用于 actions/checkout v7 及更高版本,以默认降低这些风险,并在必要时选择退出该保护的时间。

在从这些工作流之一中签出拉取请求代码之前,或在 actions/checkout 上设置 allow-unsafe-pr-checkout 输入之前,请先阅读 pull_request_target

pull_request_target 事件的风险

pull_request_target 触发的工作流会以提升的信任级别运行:作业会获得基础仓库的 GITHUB_TOKEN、对仓库和组织机密的访问权限,以及对默认分支缓存的写入权限。 这与赋予 push 这类只能由协作者触发的事件的信任相同,而这也正是 pull_request_target 对响应来自派生仓库的拉取请求的自动化操作非常有用的原因,例如添加标签、分类处理,或发布经过身份验证的状态检查。

若要了解为什么默认情况下它是安全的,以及这种安全性通常是如何被破坏的,请将pull_request_targetpull_request对照查看。

pull_request 事件(以及 pull_request_reviewpull_request_review_comment)比较特殊:它会使用来自拉取请求中的合并提交的工作流文件来运行工作流。 对于从分支打开的拉取请求,该提交由没有对基存储库的写入访问权限的人员控制。 若要安全地运行不受信任的工作流代码, GitHub 将这些事件限制为只读 GITHUB_TOKEN的、隐瞒对其他机密的访问权限,并应用分叉审批策略以防止计算滥用。 有关详细信息,请参阅“触发工作流的事件”。 默认情况下,在 pull_request 工作流中,actions/checkout 也会检出拉取请求的合并提交,因此检出的代码与运行的工作流保持一致。

pull_request_target 做出了一个关键但细微的更改:工作流以及任何未指定 ref 的后续 actions/checkout 调用,都将采用 基础仓库的默认分支,而不是来自拉取请求。 由于只有默认分支中受信任的代码运行,因此可以安全地授予机密和读/写令牌。 默认情况下,不会执行分叉中的代码。

当工作流作者重写此默认值以运行分支代码时,将带来风险。 开发人员经常选择 pull_request_target,因为他们希望让 fork 的 pull request 经过 CI 访问密钥,例如运行需要私有注册表的测试。 为此,它们将 actions/checkout 指向拉取请求的 head,而不是默认分支,这种做法是不安全的:

# INSECURE. Provided as an example only.
on:
  pull_request_target:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - name: Test
        run: make test

单独签出步骤不会执行不受信任的代码。 工作流文件本身仍来自默认分支。 该漏洞在运行已签出到当前工作目录的代码的 下一 步中完成。 在这里, make test 执行从拉取请求头获取的命令 Makefile 。 攻击者只需发起一个来自某个派生仓库的拉取请求,而该仓库的 Makefile(或构建脚本、测试命令、依赖项或配置文件)中包含恶意命令。 然后,这些命令使用基本存储库的机密和令牌运行。

这种模式被称为“pwn 请求”,并且一直是多起供应链攻陷事件的根源。 有关详细信息,请参阅防止 pwn 请求(来自 GitHub Security Lab)。 常见的易受攻击的形状包括:

  • actions/checkoutref: ${{ github.event.pull_request.head.sha }}ref: refs/pull/${{ github.event.pull_request.number }}/merge)中签出拉取请求的头提交或合并提交,然后构建、测试或以其他方式运行生成的结果。
  • repository: 设置为 fork(repository: ${{ github.event.pull_request.head.repo.full_name }}),以直接拉取该 fork 的分支。
  • actions/checkout 之外获取拉取请求代码(例如使用 git fetchgh pr checkout,或从 fork 的 pull_request 运行中下载构件),然后运行这些代码。

Pwn 请求也并非 pull_request_target 所独有。 使用机密运行的任何事件都可以在签出或下载和执行不受信任的代码时引入 pwn 请求。 例如,获取并运行派生仓库拉取请求代码的 issue_commentworkflow_run 工作流也会以相同的方式受到攻击。 workflow_run工作流应将其他工作流上传的项目视为不受信任的数据,因为它们的内容可能来自分叉。

决定是否使用 pull_request_target

某些工作流需要以更高的信任级别签出 fork 拉取请求中的代码,而这正是最初创建 pull_request_target 的原因。 例如,生成需要私有制品注册表的覆盖率报告,或针对拉取请求引入的更改生成并运行需经过身份验证的检查。 在使用 pull_request_target 或在 actions/checkout 中启用 allow-unsafe-pr-checkout 标记之前,请先考虑以下问题。

  • 是否可以改用 pull_request pull_request 由与 pull_request_target 相同的事件触发,并运行来自 pull_request 合并分支的工作流代码。 它通过上面详述的保护从分支拉取请求安全执行此操作。 如果不需要其他机密访问,请使用 pull_request。 更复杂的工作流可以重构为将对拉取请求代码的潜在危险处理与对机密信息的访问分开。 有关详细信息,请参阅防止 pwn 请求GitHub Security Lab。

  • 签出后的代码是否曾被执行过? 这是引发“pwn 请求”漏洞的缺陷。 最常见的是借助 actions/checkout,先将拉取请求的 head 签出到工作目录中,然后再运行该代码。 除非设置了 path 输入,否则 actions/checkout 会将代码写入 $GITHUB_WORKSPACE 目录,该目录通常是随后运行命令时的工作目录。 执行不限于你自己的步骤:生成和测试命令(例如npm install``npm run build,以及代码附带的配置文件和依赖项)都可以运行受攻击者控制的代码。 执行不需要明显的生成步骤。 必须确保检出的代码始终仅作为数据进行检查,并且在使用 pull_request_target 事件之前绝不能执行

强化pull_request_target工作流

如果已确认需要 pull_request_target,请应用这些控制来限制此高风险事件的影响。 无论工作流是否检出拉取请求代码,这些都适用。

  • 限制机密。 确认在 GITHUB_TOKEN 上设置的权限遵循最小权限原则,并且工作流仅使用必要的存储库和组织机密。 有关详细信息,请参阅“在工作流中使用 GITHUB_TOKEN 进行身份验证”。

  • 了解对缓存的影响。GITHUB_TOKEN 和已配置的密钥外,在 pull_request_target 上运行的工作流也对默认分支上与其他工作流共享的缓存具有写入权限。 从 pull_request_target 事件中恶意更改此缓存可能会影响其他无关工作流的执行。

  • 确保基础计算是隔离的和临时的。 如果使用自托管运行器,则必须确认该运行器环境已与内部资源适当隔离,并且不会在不同的 GitHub Actions 运行之间重复使用。 有关详细信息,请参阅“安全使用指南”。

  • 关卡在审批后运行。 pull_request_target 工作流可以受只有具有写入权限的用户才能添加的必需 label 限制。 这一点在GitHub Security Lab防止 pwn 请求的指南中有详细说明。

  • 强制实施 GitHub Actions 安全最佳做法。 除了 pwn 请求的特定风险外,其他常见漏洞(如命令注入)可能存在并影响在此特权事件中执行的代码。 有关详细信息,请参阅 保障 GitHub Actions 和工作流的安全:不受信任的输入GitHub Security Lab。 若要识别并主动防范常见 GitHub Actions 漏洞,请为 GitHub Actions 启用 CodeQL。 有关详细信息,请参阅“配置代码扫描的默认设置”。

选择退出内置保护

如果已处理上述问题并确认工作流需要 pull_request_target 并安全使用它,则可以选择退出 actions/checkout 保护。 将 allow-unsafe-pr-checkout: true 设置为 actions/checkout 输入参数后,即可检出来自派生仓库的拉取请求 head 引用。 仅在确认检出的代码绝不会被执行后,才可执行此操作。 该输入被有意命名,便于在代码评审和静态分析中发现。

此保护仅涵盖分叉拉取请求 refs。 检出其他不受信任的代码(例如不相关的第三方仓库)、使用 git fetchgh pr checkout 获取代码,或运行下载的构建产物,不在 actions/checkout 检查的涵盖范围内。

限制pull_request_target的使用

存储库、组织和企业管理员可以使用工作流执行保护来控制哪些事件和参与者可以触发工作流。 如果某个存储库对于 pull_request_target 没有任何正当用途,那么对其进行限制就能消除这种风险,无论单个工作流是如何编写的。