できない.dev

Fork からの PR で GitHub Actions の Secrets が使えない

セキュリティ上、`pull_request` イベントは fork 側のワークフローに secrets を渡さない仕様。
`pull_request_target` への切り替え、信頼境界を切り分けたジョブ設計、Environments での承認ゲートを組み合わせて安全に解決する。

#github-actions#secrets#fork#workflow

公開:

要約

Fork からの PR で secrets が undefined になるのは 意図された仕様
悪意ある PR が secret を盗まないように、pull_request イベントでは fork 側ワークフローに secrets を渡さない設計になっている。pull_request_target への切り替え、ジョブ分離、Environment 承認制を組み合わせて安全に対処する。

よくある原因

  1. pull_request の仕様: fork PR の pull_request イベントで secrets.FOO は常に空文字を返す。
    これは automatic token authentication で明示された挙動。
  2. GITHUB_TOKEN 縮小: 同イベントで GITHUB_TOKEN も実質 read 相当の権限まで絞られ、コメントや label の付与もできなくなる。
  3. ワークフロー混在: secrets を使うデプロイ処理を fork PR と同じワークフローで動かしており、毎回 fork PR で失敗する。
  4. pull_request_target の checkout 罠: pull_request_target は base ブランチで動くため、actions/checkout を素で書くと PR 側コードを落とせない。
    逆に PR 側コードを安易に落として secrets と組み合わせると重大な脆弱性になる。

解決策

1. ジョブを分離する

lint / typecheck など PR 側コードを実行するジョブは pull_request のまま、secrets を使う E2E や preview deploy だけを pull_request_target に切り出す。

on:
  pull_request:        # PR コードを実行する側(secrets なし)
  pull_request_target: # secrets を使う側(PR コードは実行しない)
jobs:
  lint:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run lint
  preview-deploy:
    if: github.event_name == 'pull_request_target'
    environment: preview
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
      - run: ./scripts/deploy-preview.sh
        env:
          PREVIEW_TOKEN: ${{ secrets.PREVIEW_TOKEN }}

2. PR コードを実行しないジョブ設計にする

Events that trigger workflowspull_request_target 節のとおり、pull_request_target は base ブランチの状態で動くことが前提。PR 側コードを checkout して secrets と同居させる構成は避ける のが鉄則。

3. Environment で承認ゲート

Settings → EnvironmentsRequired reviewers を設定すると、その environment を要求する job は maintainer が手で承認するまで開始されない。
fork PR でも secrets を解決する処理は Environment 越しに通す設計にすれば、人手承認なしには走らない。

4. push トリガーに退避する

on:
  push:
    branches: [main]

リリースやデプロイの本処理は push トリガーに移すと、fork PR から動かす必要が無くなり secrets 問題は根本的に消える。

この記事は役立ちましたか?