Skip to main content
Skip to content

安全地使用 pull_request_target

了解pull_request_target事件的安全风险。

本指南可帮助你评估工作流是否应使用 pull_request_target 事件并了解所涉及的安全风险。 它还介绍了 GitHub 默认情况下适用于这些风险的保护,以及如何在必要时选择退出这些保护。

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的、隐瞒对其他机密的访问权限,并应用分叉审批策略以防止计算滥用。 默认情况下,在 actions/checkout 工作流中,pull_request 也会检出拉取请求的合并提交,因此检出的代码与运行的工作流保持一致。

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

当工作流作者重写此默认值以运行分支代码时,将带来风险。 开发人员经常选择 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 的默认策略

为了帮助保护工作流免受不受信任的拉取请求的影响,GitHub 提供了一项默认事件策略,用于在公共仓库中阻止 pull_request_target 事件。

默认策略的工作原理

对于尚未具有适用的 Actions 事件策略的公共仓库,GitHub 会添加一项默认策略,以阻止由 pull_request_target 触发的工作流。 有关策略的详细信息,请参阅 关于操作策略

默认策略:

  • 不适用于专用或内部存储库。
  • 不替换已配置的适用事件策略。
  • 当前在 评估 模式下运行。 在此模式下,工作流运行将继续运行,但你可以使用策略见解来标识强制实施后将被阻止的运行。

2026 年 11 月 2 日,GitHub将为在正式发布前使用默认策略的受影响存储库强制实施默认pull_request_target策略。

在强制实施之前查看影响

当策略处于评估模式时,请查看策略见解,以识别使用 pull_request_target 且在强制实施该策略时会被阻止的工作流。

对于每个受影响的工作流,决定是否 pull_request_target 仍需要:

  • 如果工作流不需要 pull_request_target,请将其更新为在适当情况下使用更安全的事件,例如 pull_request
  • 如果工作流必须继续使用 pull_request_target,请创建或更新显式允许 pull_request_target的适用 Actions 事件策略。
  • 如果不想允许 pull_request_target,请保留默认策略。 强制实施后, GitHub 将阻止该事件触发的工作流。

警告

仅在必要时才允许 pull_request_target 。 由此事件触发的工作流不应在可访问仓库机密或特权 GITHUB_TOKEN 的情况下,检出、构建或运行来自不受信任拉取请求的代码。

有关配置事件策略和查看见解的详细信息,请参阅 控制谁可以执行 GitHub Actions 工作流。 若要以编程方式管理策略,请参阅 GitHub Actions 策略的 REST API 端点

决定是否使用 pull_request_target

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

  • 了解对缓存的影响。 为了降低缓存中毒的风险,由 pull_request_target 触发的工作流对默认分支作用域内的缓存仅具有只读访问权限。 这些工作流可以还原现有缓存条目,但无法创建或覆盖它们,因此它们不会影响通过共享缓存执行其他无关工作流。 如果此类工作流尝试保存缓存,则保存失败,但步骤和作业继续,失败报告为工作流日志中的警告。 如果工作流需要填充缓存,请从在受信任的触发器(例如 push)上运行的工作流中保存该缓存。 工作流或作业可以通过显式声明一个具有写入权限的 cache-mode,选择不受这一只读限制的约束;但如果在 pull_request_target 工作流上这样做,就会重新引入这种限制原本旨在防止的缓存投毒风险。 有关详细信息,请参阅“依赖项缓存参考”。

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

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

选择退出内置保护

如果已处理上述问题并确认工作流需要 pull_request_target 并安全使用它,则可以选择退出 默认事件策略和保护 actions/checkout

allow-unsafe-pr-checkout: true 设置为 actions/checkout 输入参数后,即可检出来自派生仓库的拉取请求 head 引用。 仅在确认检出的代码绝不会被执行后,才可执行此操作。 该输入被有意命名,便于在代码评审和静态分析中发现。

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