GitHub ワークフローのセキュリティリスク監査
GitHub Actions を標的としたサプライチェーン攻撃は、理論上の脅威から日常的な現実へと変わりつつあります。2025年3月に発生した tj-actions/changed-files の侵害事例は、単一の汚染されたアクションがいかに迅速に数千のリポジトリにわたってシークレットを漏洩させうるかを示しました。その1年後、Trivy-action インシデントでも同様のパターンが繰り返され、攻撃者は77個中76個のバージョンタグに悪意のあるコードを強制プッシュしました。
すでに本番環境でワークフローを運用している場合、このチェックリストを活用することで、パイプライン全体を再設計することなく、最も一般的な脆弱性を発見できます。
重要なポイント
- ワークフローレベルで
permissions: {}を設定し、各ジョブに必要な最小限のスコープのみを付与する。 ${{ github.event.* }}の値をシェルコマンドに直接補間しない——代わりに環境変数を経由して渡す。- サードパーティのアクションはフルコミット SHA にピン留めし、
pull_request_targetとフォークコードのチェックアウトを組み合わせることを避ける。 - 静的なクラウド認証情報を OIDC に置き換え、パブリックリポジトリでセルフホストランナーが信頼されていないコードを実行しないよう制限する。
まず GITHUB_TOKEN の権限を確認する
任意のワークフローファイルを開き、トップレベルの permissions ブロックを確認します。このブロックが存在しない場合、組織のデフォルト設定が適用されます——2023年2月以前に作成された組織では、多くの場合デフォルトが読み書き権限になっています。
修正方法は簡単です:
permissions: {} # ワークフローレベルですべてを拒否
jobs:
build:
permissions:
contents: read # ジョブに必要なものだけを付与
ワークフローレベルで permissions: {} を設定することで、各ジョブに必要な権限を明示的に宣言することが強制されます。コードを読み取るだけのジョブが、リポジトリへの書き込みアクセスを持つ GITHUB_TOKEN を保持すべきではありません。
シェルコマンド内の信頼されていない入力を探す
ワークフローファイル内の run: ブロックに含まれる ${{ github.event を検索してください。これは最も一般的なスクリプトインジェクションのパターンです:
# リスクあり: 攻撃者が PR タイトルを制御できる
- run: echo "Checking ${{ github.event.pull_request.title }}"
安全な代替手段は、信頼されていない値を中間の環境変数を通じて渡すことです:
- name: Check PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "Checking $TITLE"
この方法では、値がシェルスクリプトに補間されるのではなく環境変数経由で渡されるため、入力に含まれるシェルのメタ文字がコマンドを実行するために悪用されることを防げます。また、ユーザーが制御するコンテンツを処理するステップにおける GITHUB_ENV および GITHUB_PATH への書き込みにも注意が必要です——攻撃者はこれらを利用して、後続のステップに環境変数や悪意のあるバイナリを注入する可能性があります。
pull_request_target の使用状況を監査する
pull_request_target はベースブランチのシークレットにアクセスできる状態で実行されるため、フォークからの PR にコメントする必要があるワークフローに有用です。リスクはトリガー自体にあるのではなく、フォークのコードをチェックアウトと組み合わせることにあります:
# 危険な組み合わせ
on: pull_request_target
jobs:
test:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # 攻撃者のコードを実行
- run: npm test # シークレットへのアクセスを持った状態で
pull_request_target を使用する場合は、PR ブランチのコードを実行するステップが存在しないことを確認してください。GitHub は2025年末にこの問題を部分的に緩和しましたが、フォークのチェックアウトと組み合わせた場合、このトリガーは依然として高リスクです。
Discover how at OpenReplay.com.
サードパーティアクションのピン留めを確認する
tj-actions/changed-files の侵害が成功したのは、チームが @v35 のような変更可能なタグを参照していたためです。タグの値は気づかれることなく書き換えられる可能性があります。フルコミット SHA へのピン留めによってこれを防ぐことができます:
# 脆弱
- uses: tj-actions/changed-files@v35
# 安全
- uses: tj-actions/changed-files@d6babd6899969df1a11d14c368283ea4436bca78
GitHub は現在、SHA ピン留めを強制し、ピン留めされていないアクションを使用するワークフローを失敗させる組織レベルのポリシーを提供しています。組織レベルの Settings → Actions → General を確認してください。ピン留めだけでは十分ではありません——新しいアクションバージョンを採用する前に7〜14日間のクールダウン期間を設けることも検討してください。ほとんどのサプライチェーン侵害は1週間以内に検出されるためです。
セルフホストランナーの露出を評価する
セルフホストランナーはデフォルトで永続的です。侵害されたワークフローは、ジョブをまたいで存続するバックドアをインストールする可能性があります。重要な問いは、パブリックリポジトリがセルフホストランナーを使用しているかどうかです——もしそうであれば、任意のコントリビューターがインフラ上で任意のコードを実行する PR を提出できます。
Settings → Actions → Runners でランナーグループを確認し、パブリックリポジトリが除外されていることを確認してください。機密性の高いワークロードには、各ジョブ終了後に破棄されるジャストインタイム(JIT)ランナーの使用を推奨します。
長期有効なクラウド認証情報を OIDC に置き換える
ワークフローがシークレットとして保存された静的な認証情報を使用して AWS、Azure、または GCP に認証している場合、それらの認証情報はそのジョブ内のすべてのアクションとステップに公開されています。OpenID Connect(OIDC) は、実行時に短命でジョブスコープのトークンを発行することでこの問題を解消します。盗まれるシークレットも、手動でローテーションする認証情報も存在しません。
その他の確認事項
- アーティファクトの取り扱い: ダウンストリームのステップが明示的にトークンを必要としない限り、
actions/checkoutにpersist-credentials: falseを設定してください。これにより、チェックアウトトークンがローカルの Git 設定内で後続のワークフローステップから引き続き利用可能な状態になることを防ぎます。 - 環境の保護: デプロイメントシークレットは、プレーンなリポジトリシークレットとしてではなく、必須レビュアーを設定した GitHub Environments に保存してください。
- アーティファクトの証明: 公開パッケージに対しては、GitHub のアーティファクト証明機能がビルドとそのソースワークフローの間に検証可能なリンクを提供します。
- OpenSSF Scorecards: Scorecards アクションは、トークン権限、ピン留めされたアクション、スクリプトインジェクションの自動チェックを実行するように設定でき、リグレッションを自動的に検出するのに役立ちます。
どこから始めるか
.github/workflows/ 全体で以下のパターンを検索してください:欠落しているか write-all に設定されている permissions ブロック、run: ステップ内の ${{、pull_request_target トリガー、フル SHA のないアクション参照。この4つのチェックにより、新しいツールを導入することなく、ほとんどのリポジトリで最もリスクの高い問題を発見できます。
まとめ
GitHub Actions のセキュリティ確保には、パイプラインの全面的な書き直しは必要ありません——実際に発生したインシデントのほとんどは、過度に広いトークンスコープ、信頼されていない入力の安全でない補間、変更可能なアクション参照、信頼されていないコードに公開されたランナーという少数の繰り返されるミスに起因しています。上記のチェックを実施することで、防御可能なベースラインを確立できます。そのベースラインに OpenSSF Scorecards や類似のツールによる自動スキャンを組み合わせることで、リグレッションが本番環境に到達する前に検出できるようになります。
よくある質問
GitHub のコード検索は組織全体のクエリをサポートしています。`path:.github/workflows` にスコープを絞り、`pull_request_target`、`permissions: write-all`、または `github.event.pull_request.title` などの用語を検索してください。より詳細な分析には、Octoscan、zizmor、OpenSSF Scorecards アクションなどのツールがリポジトリ全体をスキャンし、トークンスコープ、ピン留めされていないアクション、インジェクションシンクについて自動的にレポートできます。
いいえ、ただしアップデートが明示的になります。バージョン間の差分を確認した後、SHA をいつ更新するかを自分で決定します。Dependabot や Renovate などのツールは、ピン留めされた SHA を自動的に更新するプルリクエストを作成できるため、メンテナンスの負担なしにピン留めの安全性を享受できます。これらのアップデートをマージする前に7〜14日間の遅延を設けることで、新たに侵害されたリリースへの露出をさらに軽減できます。
AWS、Azure、GCP、およびほとんどの主要プロバイダーに対しては、そうです。OIDC トークンは短命で、特定のワークフロー実行にスコープが限定されており、後で使用するために流出させることができません。セットアップにはクラウドプロバイダーでの信頼関係の設定が必要ですが、ローテーションの負担を解消し、ワークフローが侵害された場合の影響範囲を限定します。静的なシークレットが有効な代替手段となるのは、OIDC がサポートされていない場合に限られます。
`pull_request` トリガーはフォークのコンテキストで実行され、ベースリポジトリのシークレットにはアクセスできないため、信頼されていないコードのテスト実行に安全です。`pull_request_target` トリガーはベースリポジトリのコンテキストでシークレットにアクセスできる状態で実行され、PR へのラベル付けなどのタスクを想定しています。避けるべき危険な組み合わせは、`pull_request_target` とフォークのコードのチェックアウトを組み合わせることです。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.