# 安全地使用 pull_request_target

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

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

在从这些工作流之一中签出拉取请求代码之前，或在 `pull_request_target` 上设置 [](/zh/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target) 输入之前，请先阅读 `allow-unsafe-pr-checkout`。

## pull\_request\_target 事件的风险

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

若要了解为什么默认情况下它是安全的，以及这种安全性通常是如何被破坏的，请将`pull_request_target`与[`pull_request`](/zh/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request)对照查看。

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

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

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

```yaml
# 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 请求](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/)（来自 GitHub Security Lab）。 常见的易受攻击的形状包括：

* 在 `actions/checkout`（`ref: ${{ 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 fetch`、`gh pr checkout`，或从 fork 的 `pull_request` 运行中下载构件），然后运行这些代码。

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

## 决定是否使用 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 请求](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/#preventing-pwn-requests)（来自 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 进行身份验证](/zh/actions/tutorials/authenticate-with-github_token#modifying-the-permissions-for-the-github_token)”。

* **了解对缓存的影响。** 为了降低缓存中毒的风险，由 `pull_request_target` 触发的工作流对默认分支作用域内的缓存仅具有只读访问权限。 这些工作流可以还原现有缓存条目，但无法创建或覆盖它们，因此它们不会影响通过共享缓存执行其他无关工作流。 如果此类工作流尝试保存缓存，则保存失败，但步骤和作业继续，失败报告为工作流日志中的警告。 如果工作流需要填充缓存，请从在受信任的触发器（例如 `push`）上运行的工作流中保存该缓存。 有关详细信息，请参阅“[依赖项缓存参考](/zh/actions/reference/workflows-and-actions/dependency-caching#cache-access-for-low-trust-workflow-triggers)”。

* **确保基础计算是隔离的和临时的。** 如果使用自托管运行器，则必须确认该运行器环境已与内部资源适当隔离，并且不会在不同的 GitHub Actions 运行之间重复使用。 有关详细信息，请参阅“[安全使用指南](/zh/actions/reference/security/secure-use#hardening-for-self-hosted-runners)”。

* **强制实施 GitHub Actions 安全最佳做法。** 除了 pwn 请求的特定风险外，其他常见漏洞（如命令注入）可能存在并影响在此特权事件中执行的代码。 有关详细信息，请参阅 [保障 GitHub Actions 和工作流的安全：不受信任的输入](https://securitylab.github.com/resources/github-actions-untrusted-input/)GitHub Security Lab。 若要识别并主动防范常见 GitHub Actions 漏洞，请为 CodeQL 启用 GitHub Actions。 有关详细信息，请参阅“[配置代码扫描的默认设置](/zh/code-security/how-tos/find-and-fix-code-vulnerabilities/configure-code-scanning/configure-code-scanning)”。

## 选择退出内置保护

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

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

## 限制pull\_request\_target的使用

如果仓库对 `pull_request_target` 没有正当用途，那么限制该事件即可消除这种风险，无论各个工作流是如何编写的。 管理员可以使用工作流执行保护来控制哪些事件和执行组件可以触发工作流。 有关详细信息，请参阅适用于仓库（[工作流执行保护机制](/zh/repositories/managing-your-repositorys-settings-and-features/actions-policies/workflow-execution-protections)）、组织（[](/zh/organizations/managing-organization-settings/actions-policies/workflow-execution-protections)。