コンテンツにスキップ
Zenn Dev Kou Pg 0131 Articles Gha Script Injection

【GitHub Actions】スクリプトインジェクションの実践例

  • URL: https://zenn.dev/kou_pg_0131/articles/gha-script-injection
  • 日付: 2026-06-26
  • Tier: Tier 3
  • 要旨: GitHub Actionsのrunブロック内で${{}}式を直接展開するとスクリプトインジェクションが可能になることを、PR タイトルとブランチ名を使った実際の攻撃例で示した記事。PRタイトルを「"; echo INJECTED"」にすると式が展開されて任意コードが実行され、ブランチ名では${IFS}でスペース代わりにして同様の攻撃が成立する。対策はrunブロック内で${{}}を使わずシェル環境変数経由で値を渡すことで、${{ env.* }}を使っても同様に展開されるため誤解を招きやすい。プライベートリポジトリも侵害されたGitHub App経由で攻撃可能であり、ghasec・actionlint・zizmor等の静的解析ツールで自動検出できる。

詳細

  • 脆弱なパターン: run内で ${{ github.event.pull_request.title }} や ${{ github.head_ref }} を直接使用
  • 攻撃例1(PRタイトル):
    • タイトルを ‘"; echo INJECTED"’ に設定
    • 展開後: echo “PR title is “; echo INJECTED”” となり任意コードが実行される
  • 攻撃例2(ブランチ名):
    • ブランチ名を ‘main";echo${IFS}INJECTED"’ に設定(ブランチ名にスペース不可なので${IFS}で代用)
    • 展開後: echo “PR Head Ref is main”;echo${IFS}INJECTED"" となる
  • 対策: 環境変数経由で渡してシェル変数として参照する
    • 誤: run: echo “${{ env.PR_TITLE }}” with env: PR_TITLE: ${{ … }} ← ${{ env.* }}も実行時に展開されるため不可
    • 正: run: echo “${PR_TITLE}” with env: PR_TITLE: ${{ … }} ← シェル変数を使う
    • github.head_refはGITHUB_HEAD_REFというデフォルト環境変数が既に用意されているので自前定義不要
  • プライベートリポジトリも危険: Contents:Write + Pull Requests:Writeを持つ侵害済みGitHub App経由で攻撃可能(Workflows:Writeなしでも任意コード実行が成立)
  • 静的解析による自動検出:
    • ghasec: script-injection ルール
    • actionlint: expression チェック
    • zizmor: template-injection ルール(confidence: High)
  • 参考文献: 「GitHub CI/CD実践ガイド」第15章 GitHub Actionsのセキュリティ