GitHub Actionsのfork PRでSecretsが使えない理由と安全なCI設計

fork PRでsecret未提供のまま検証を通す範囲と、信頼済み処理にsecretが必要な範囲を分離する。G1099のpull_request_targetを本稿に統合し、別記事化しない。

AIものさし編集部

forkからのPull RequestだけCIが失敗し、同じ変更を本体repositoryで動かすと成功する。そんなとき、secretの登録場所を増やす前に、どのイベントがどのコードを実行しているか確認します。fork PRでsecretが渡らないことは、設定漏れとは限りません。信頼していない変更を認証情報から分けるための境界が関係します。

この記事では、PRのテストを続けたい保守担当者が、secretなしで実行できる検証と、信頼済みの処理だけに任せる作業を分ける手順を扱います。OIDC tokenの発行やcloud側trust policyの診断は別の問題です。

最初に成功runと失敗runの条件を並べる

ログにsecret値を表示して有無を調べてはいけません。イベント名、PRの送信元repository、workflowの参照元、失敗step、必要な認証先を記録します。空文字や認証エラーだけから「secretが存在しない」と結論づけず、そのrunへ提供される条件を見ます。

表は横にスクロールできます

確認項目比較する内容分かること
イベントpull_request、push、pull_request_target等実行の信頼境界が同じか
PR送信元本体のbranchかforkか外部変更を扱う経路か
実行するコードbase側、PRのhead、merge commitsecretを使う処理へ外部変更が混ざるか
失敗stepprivate registry、API、通知等secretが必要な作業を限定できるか
利用環境public/private、組織のActions設定適用されるポリシーを確認する先

GitHubのイベント資料では、fork由来のpull_requestでは通常GITHUB_TOKEN以外のsecretをrunnerへ渡さず、GITHUB_TOKENの権限も制限すると説明しています。private repository等のfork設定では扱いを変えられる場合があるため、一般的な既定動作と自分の組織の設定を混同しないでください。GitHub:Events that trigger workflows(新しいタブで開く)

runの実行承認と、secretを提供する設定は別に確認します。「承認されたから通常のpushと同じcredentialが使える」と読み替えないことが大切です。

secretを必要とする作業を小さく分ける

例えば、公開依存関係だけで行う構文検査と、private registryへ接続する結合テストでは、必要な認証が違います。CI全体を「secretがないと何もできない」と扱わず、失敗stepから入力と権限を分解します。以下は設計を考える架空例で、実行確認済みの構成ではありません。

表は横にスクロールできます

作業fork PRでの検討残す確認
公開依存だけの構文・単体検査secretなしで実行できる範囲を切り出すinstall scriptや実行環境の安全性
private依存が必要な検査代替fixtureや信頼済み経路を検討省略した検査と、後で実行する条件
label・triage等の管理処理base側の信頼済み処理で扱う必要最小のtoken権限と入力の扱い
公開・配布・本番接続PRの検証と別の信頼境界で扱う対象commit、実行者、承認・復旧条件

secretをダミー値に置き換えて検査を通す場合も、実際の結合テストと同じ保証にはなりません。PR上で「単体検査は完了、private接続検査は未実施」と区別し、未実施の確認がどの段階で必要かを残します。失敗を見えなくするために無条件成功へ変えると、必要な検証が実行されたか分からなくなります。

pull_request_targetへ変える前にコードの実行元を見る

pull_request_targetは、fork PRに対するlabel付けなど、base側の権限を使う管理処理で検討されるイベントです。しかし、secretを使えるようにするだけの置換先ではありません。GitHubは、base側の権限を持つrunで外部PRのコードを取得し、そのコードをbuild・test等で実行する構成を危険な形として説明しています。GitHub:Securely using pull_request_target(新しいタブで開く)

checkout自体だけでなく、その後に実行するMakefile、package script、依存関係、設定ファイルまで追います。見た目は固定された「テスト実行」でも、読み込む内容がPRで変更できれば、その変更が認証情報へ到達する経路になり得ます。

表は横にスクロールできます

問い確認する箇所
外部PRのheadを取得しているかcheckoutのref・repository、独自fetch処理
取得したコードを実行するかbuild/test/install、設定・依存読込み
外部入力がコマンドへ入るかPR本文、branch名、生成ファイルの扱い
artifact経由で外部内容を受け取るか生成元runと内容の信頼性、展開後の用途

取得後に実行する経路が残るなら、管理処理とPRコードの検証を分離する設計へ戻ります。別workflowに分けても、権限のある側で未信頼artifactを実行すれば境界は保てません。GitHubの安全性資料も、workflow_run等で受け取ったartifactを慎重に扱うよう案内しています。GitHub:Secure use reference(新しいタブで開く)

イベントポリシーによる停止も分けて確認する

workflowが起動しない場合は、secret未提供とは別に、組織・repositoryのActions event policyが適用されていないかを確認します。ポリシーで止まっているrunへsecretを追加しても原因は解消しません。insightsや設定の閲覧権限がない場合は、対象workflowとイベントを添えて管理者へ確認します。

2026年10月20日時点のGitHubのpull_request_target資料は、対象public repositoryに対する既定ポリシーと、evaluateからenforcementへ移る予定を案内しています。適用条件や日程は変更され得るため、変更前に同資料と自分の環境のpolicyを確認してください。この記事を理由にポリシーを解除したり、unsafeなcheckoutを許可したりする必要はありません。

確認の終点を「認証成功」だけにしない

変更後は、secretなしのfork PR、本体branch、必要なら信頼済みの認証処理という経路を別々に確認します。認証先へ接続するテストは、許可された検証環境で行い、secretやtokenをログへ出さないでください。

表は横にスクロールできます

経路期待する結果実際に記録すること
fork PRの検証許可したsecretなし検査が動く実行済み/省略した検査
base側の管理処理必要な管理操作だけを行うPRコードを実行しないことの確認
信頼済み認証処理指定commitと必要範囲だけを扱う対象・権限・結果・残る未確認

完了とは、すべてのPRへsecretを渡せたことではありません。どの入力を信頼し、どの検査をいつ実行するかが説明でき、外部変更を権限のある実行経路へ無条件に持ち込まないことです。本文は公式仕様に基づく設計・診断手順で、特定repositoryのworkflow実行や安全性監査を完了した報告ではありません。