securityopsweb
GitHub Actionsハードニング|CIを攻撃面にしないチェックリスト(permissions / SHA pin / PRイベント)
|
5 min read
GitHub Actionsは権限と秘密情報が集まる実行環境。permissions最小化、外部ActionのSHA pin、fork PRとpull_request_targetの隔離、Environmentによるデプロイ承認など、まず止血する実務チェックリスト。
目次
- 結論(Conclusion)
- 背景(Explanation)
- 実務手順(Practical Guide)
- 手順1:permissions を明示して最小化(最優先)
- 手順2:外部ActionをSHAでpin
- 手順3:fork PR を安全に扱う(pull_request_target は原則避ける)
- 手順4:長期secretsを前提にしない設計へ
- 手順5:デプロイをEnvironmentでゲートする
- 手順6:self-hosted runner は爆発半径が大きい
- 失敗パターン(Pitfalls)
- チェックリスト(Checklist)
- FAQ
- Q1. SHA pinは本当に必要?
- Q2. pull_request_target は使える?
- Q3. 1つだけやるなら何?
- 内部リンク(Internal links)
- 参考(一次/準一次)
GitHub Actionsを固くするには、どこを優先して見直す?
結論(Conclusion)
GitHub Actionsは「ビルドする場所」ではなく、権限と秘密情報が集まる実行環境。 まず効くのはこの4つ。
permissionsを最小権限に固定- 外部Actionを commit SHAでpin
- fork PR と
pull_request_targetを 権限とコード実行の観点で隔離 - デプロイは Environment + required reviewers で止める
速度改善はこの後。
背景(Explanation)
CI事故はだいたい、次が組み合わさって起きる。
- サプライチェーン(Actions/npm/Docker)
- 不正実行(fork PR)
- 過剰権限(write token / secrets露出)
本命のリスクは「秘密が1回漏れる」ではない。 CIが踏み台になってリポジトリ/デプロイ経路が書き換えられること。
実務手順(Practical Guide)
手順1:permissions を明示して最小化(最優先)
デフォルトに依存しない。まず読み取り。
permissions:
contents: read
必要なジョブだけ権限を広げる。 例:PRコメントが必要な場合。
permissions:
contents: read
pull-requests: write
判断基準:
- 「便利だからwrite」は最短で刺される。
手順2:外部ActionをSHAでpin
やる:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
避ける:
- uses: actions/checkout@v4
判断基準:
- 動く参照(tag)は依存追加と同じ。差分が見える形にする。
手順3:fork PR を安全に扱う(pull_request_target は原則避ける)
安全寄りの基本:
- テストは
pull_request - fork PR に secrets を渡さない
pull_request_target を使うなら:
- PRのコードを実行しない
- secretsを渡さない
- コメント/ラベル等の“読み取り主体”に限定
手順4:長期secretsを前提にしない設計へ
- 不正入力で走るジョブにsecretsを置かない
- 可能ならOIDC(短期トークン)でクラウドデプロイ
手順5:デプロイをEnvironmentでゲートする
environment: production- required reviewers を必須
これで「CIが勝手に本番を触る」を止められる。
手順6:self-hosted runner は爆発半径が大きい
最低ライン:
- 使い捨て(ephemeral)に寄せる
- runnerにsecretsを置かない
- キャッシュ/ワークスペースを信頼境界で共有しない
失敗パターン(Pitfalls)
GITHUB_TOKENが過剰権限- 外部Actionをtag参照(後から中身が変わる)
pull_request_targetで secrets + PRコード実行- self-hosted runner の状態が残る
- cache key設計が甘くキャッシュ汚染
チェックリスト(Checklist)
- [ ] workflow/jobの
permissionsを明示している(最小権限) - [ ] write権限は必要ジョブだけ(コメント/リリース等)
- [ ] 外部Actionをcommit SHAでpinしている
- [ ] pin更新は週次などにまとめてレビュー
- [ ] fork PR のジョブはsecrets無しで動く
- [ ]
pull_request_targetは避ける/隔離(PRコード実行なし) - [ ] デプロイはOIDC等の短期クレデンシャルへ
- [ ] Environment + required reviewers で本番をゲート
- [ ] self-hosted runner をephemeral扱いにする/隔離
- [ ] cache key設計を見直し、汚染耐性を上げる
- [ ] インシデント時の手順(トークンローテ、workflow停止、runner隔離)がある
FAQ
Q1. SHA pinは本当に必要?
必要。tagは動く。SHAは動かない。ワークフロー依存のドリフトをレビュー可能にする。
Q2. pull_request_target は使える?
使えるが隔離が前提。secrets無し、PRコードを実行しない、読み取り主体だけ。
Q3. 1つだけやるなら何?
permissions の最小化。過剰権限トークンが一番速く事故につながる。
内部リンク(Internal links)
参考(一次/準一次)
- GitHub Docs: Workflow permissions / GITHUB_TOKEN: https://docs.github.com/actions/security-guides/automatic-token-authentication
- GitHub Docs:
pull_request_target: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#pull_request_target - OpenSSF Scorecard: https://github.com/ossf/scorecard
- GitHub Docs: OpenID Connect: https://docs.github.com/actions/security-guides/about-security-hardening-with-openid-connect