GitHub Actionsのactions.lockがGA — SHA固定でも防げない内部依存を固定する手順と限界

はじめに

GitHub Actions のワークフロー用ロックファイル actions.lock が、2026 年 10 月 28 日に一般提供(GA) になりました。Action のバージョンをコミットに解決して記録し、実行時にはその記録どおりのコミットを動かす仕組みです。

npm の package-lock.json に近いものですが、狙いは再現性よりも攻撃対策にあります。これまでの定番だった SHA 固定には、Action の内部で使われる別の Action まで固定できないという穴がありました。この記事では、その穴が何だったのか、ロックファイルでどう塞ぐのか、それでも守れない範囲はどこかを整理します。

SHA 固定で防げなかったもの

2026 年 3 月の Trivy のサプライチェーン攻撃は、この穴をよく示しています。公式アドバイザリによると、aquasecurity/trivy-action は 77 個のバージョンタグのうち 76 個が、aquasecurity/setup-trivy は 7 個すべてが悪意のあるコミットへ force push されました。trivy-action のペイロードはランナーのプロセスメモリから secrets を抜き取り、SSH 鍵やクラウドの認証情報も集めて外部へ送信しています。

タグは書き換えられるので、@v7 はもちろん @v7.0.1 のような指定でも防げません。そこで推奨されてきたのがフル SHA での固定ですが、アドバイザリは 2025 年 4 月 9 日より前のコミットへの SHA 固定も、setup-trivy が改ざんされていた約 4 時間に実行した場合は影響範囲 に挙げています。Progate の解説記事は、この理由を内部の参照で説明しています。利用者が trivy-action を v0.28.0 の SHA で固定していても、その中に uses: aquasecurity/setup-trivy@v0.2.1 というタグ参照が残っていたのです。

つまり、利用者が書いた 1 段目を固定しても、2 段目から先は他人の書き方に左右されます。

actions.lock の仕組み

gh actions-lock はワークフローを読み、内部の依存までたどって .github/workflows/actions.lock に記録します。

dependencies:
    'actions/checkout@v7.0.1':
        ref: 'v7.0.1'
        commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1'

実際のエントリには owner_id と repo_id も入り、Action が内部で使う依存は uses に並びます。ワークフロー側は actions/checkout@v7.0.1 のまま読みやすさを保てるのが、SHA を直書きする方式との大きな違いです。

実行時の検証で押さえたい点は 3 つあります。

  • ロックに登録したワークフローでは、すべての依存がロックファイルに載っている必要があり、記録されたコミットが実行されます。
  • そのコミットは、記録されたブランチまたはタグから到達できなければ拒否されます。フォークにしかないコミットを差し込む手口への対策です。
  • owner_id と repo_id により、同じ名前の裏にあるリポジトリが入れ替わっていないかを確認します。

DevelopersIO の検証(プレビュー期の v0.2.0)では、タグを別コミットへ force push しても、次の実行は固定したコミットをダウンロードしました。逆に、ロックファイルを更新せずに uses を追加すると、ジョブが 1 つも始まらず startup_failure になり、Invalid lockfile の注釈が出ています。

導入手順

GitHub CLI にログインした状態で、リポジトリのルートから実行します。

gh extension install github/gh-actions-lock
gh actions-lock
git add .github/workflows/

gh actions-lock はワークフローも書き換えます。@v7 は既定で @v7.0.1 のような具体的なバージョンに絞られるので、差分を読んでからコミットしてください。./ で始まるローカル Action はロックされず、その中で別の Action を使っているとエラーになって何も書き込まれません。--migrate-local-actions で $/ 形式へ書き換えるか、--ignore-local-actions で ./ のまま警告扱いにするかを選びます。

続けて、運用を次の順で固めます。

  1. CI で検証する: gh actions-lock --verify はファイルを書き換えず、修正時と同じ検査をします。pre-commit にはオフラインで動く --verify-local がありますが、確認するのは網羅性だけで、コミットの出自は見ません。README は verify-lockfile.yml をリポジトリへコピーする方法を勧めています。
  2. 更新の流れを決める: Dependabot は、登録済みのワークフローなら同じ PR でロックファイルも更新します。手で上げる場合は uses を書き換えて gh actions-lock を再実行します。@v4 のような可動参照を上流の最新へ進めるには --relock を使います。
  3. Require lockfile ポリシーで強制する: 全ワークフローの登録が済んだら、Enterprise・Organization・リポジトリの各レベルで有効にできます。呼び出し先の再利用ワークフローが未登録だと呼び出し元の実行も止まるので、先に確認してください。

--accept-moved は、固定したコミットが参照の履歴から外れた場合に受け入れるフラグです。README は「まず理由を確認すること、自動化では絶対に使わないこと」と釘を刺しています。

対象は changelog によると Enterprise Cloud(データレジデンシー含む)、Team、Free の各プランです。GitHub Enterprise Server は拡張機能の README で非対応とされています。

actions.lock で守れない範囲

公式情報を読むと、主に次の範囲は対象外です。

対象外内容補う方法
再利用ワークフローの呼び出しjobs.<id>.uses は固定されないフル SHA で参照する
コンテナイメージ固定されない。digest なしは警告のみ@sha256: で指定する
入力値やランナーイメージドキュメントが対象外と明記入力値もバージョンを固定する
可動参照の巻き戻しv4 などを履歴上の古いコミットへ戻す変更は検出されない差分をレビューし、具体的なタグを使う
ロック時点で悪性のコミット安全性の判定はしないロック前に依存をレビューする

このほか ./ のローカル Action もロックされません。特に注意したいのが 3 行目と 5 行目です。

Trivy の事件では、version: latest を指定した利用者も、悪意のあるバイナリが配布された時間帯は影響を受けたとアドバイザリにあります。Action のコードをコミットで固定しても、その Action が入力値に従ってダウンロードするものまでは固定されません。ツールを取得する型の Action では、入力値でバージョンを明示し、取得物の検証手段があるかも確認する必要があります。

5 行目は GitHub 自身が changelog で「ロックファイルは依存が安全かどうかを判断しない」と書いている点です。攻撃の最中に、まだロックしていない参照を gh actions-lock で新しく固定したり、移動後のタグへ --relock で進めたりすると、悪意のあるコミットをそのまま記録してしまいます。Trivy の場合、trivy-action の露出は約 12 時間でした。ロック時点の悪性コミットまでは防げないので、Progate の記事が勧めるように、公開からの日数やリリースノートを確認してから上げる運用と組み合わせる必要があります。

エンジニアへの影響

公式ドキュメントは、ロックファイルを導入したリポジトリでは SHA 固定は「もう必要ない」と説明しています。組織の「フル SHA 固定を必須にする」ポリシーも、登録済みのワークフローならロックファイルで満たせます。GitHub.com 上のステップで使う Action については、SHA とコメントが並ぶ書き方を今後新しく増やす理由は薄れました。

一方で、すぐに全リポジトリへ強制するのは勧めません。拡張機能はプレビュー期の v0.2.0 で --verify の判定を厳しくしており、仕様が動いてきた経緯があります。私なら、secrets や本番環境へのデプロイ権限を持つワークフローがあるリポジトリから順に導入し、Dependabot の PR が問題なく回ることを確認してから Require lockfile を有効にします。

組織で許可 Action を管理している場合は、許可リストも先に確認してください。公式の手順によると、登録済みのワークフローでは、タグ名で許可する設定が固定された SHA に一致しなくなります。

まとめ

actions.lock は、SHA 固定では届かなかった内部の Action まで、読みやすさを保ったまま固定します。導入の手間は小さく、タグの書き換えに対しては確かな防御になります。

ただし、守るのは「記録したコミットから変わらないこと」だけで、そのコミットが安全かどうかは判断しません。入力値で取得するバイナリやコンテナイメージも別途固定が必要です。今後は、更新を急がない運用と差分のレビューが、CI の安全を左右する部分として残ります。

今日のその他のニュース

Cursor のマーケットプレイスに改ざん版 Claude Code 拡張: 2.1.312 / 2.1.313 が 10/29〜30 に約 11 時間公開されていました。macOS では grep -n svc-telemetry ~/.ssh/authorized_keys で確認でき、Windows は PowerShell の別手順です。該当時は公式の修復スクリプトと認証情報の変更が必要です。(Cursor の告知)

Linux カーネル CVE-2026-98432: TCP 接続要求の処理にある use-after-free で、Red Hat は Critical と評価しています。2015 年の変更が原因で、修正版カーネルの提供状況はディストリビューションごとに異なります。(RHSB-2026-016、SUSE)

Northflank でセキュリティインシデント: 全環境変数が漏洩したものとして扱い、認証情報をすべてローテーションするよう案内しています。AI API キーの不正利用の報告も出ています。(ステータスページ)

ソース