SHA Pinning(SHAピン留め)は、GitHub Actionsのワークフローで使うアクションを @v7 のようなタグではなく、40文字のフルコミットSHAで参照する対策です。タグは後から別のコミットへ付け替えられるため、アクションのリポジトリが乗っ取られると、タグ参照のワークフローは次の実行から攻撃者のコードを取り込みます。2025年3月の tj-actions/changed-files 侵害はまさにこの形でした。
この記事では、SHA固定の書き方、強制設定「Require actions to be pinned to a full-length commit SHA」、一括固定ツール pinact、固定後の更新を回す Dependabot を整理したうえで、2026年10月14日に公開プレビューとなったワークフロー依存のロックファイル(.github/workflows/actions.lock)とSHA固定をどう使い分けるかを、公式ドキュメントとツールの実行結果をもとに説明します。
まとめ:SHA Pinningの要点
- 書き方は
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1。SHAは40文字すべて書き、同じ行にバージョンコメントを残します。 - 強制はリポジトリとOrganizationの Settings → Actions → General、Enterpriseでは Policies → Actions のチェックボックスで行います。違反したワークフローは実行時に失敗します。ロックファイルを使わない場合、タグ参照のままでよいのは Reusable Workflow の呼び出しだけで、社内アクションのブランチ参照(
@main)も止まります。 - 既存ファイルの一括固定と検査は pinact(
pinact run/pinact run --check)、固定後の更新は Dependabot か Renovate に任せます。SHA固定したアクションには Dependabot のセキュリティアラートが出ないため、更新PRを止めないことが前提です。 - GitHub.com(Free・Team・Enterprise Cloud)では、2026年10月からロックファイルが公開プレビューで使えます。公式ドキュメントは、ロックファイルを採用したリポジトリではワークフローファイル内のSHA固定は不要としています。ただし pre-1.0 のプレビューで、GHESは対象外です。
- 導入は「pinactでCI検査 → 一括固定 → 更新の自動化 → 強制設定」の順が安全です。強制設定には除外指定も評価モードも無いため、最初に入れると依存先の都合でワークフローが止まります。
SHA Pinningの意味とタグ参照が危ない理由
ワークフローの uses: には、ブランチ名・タグ名・コミットSHAのどれでも書けます。このうちブランチとタグは「今どのコミットを指しているか」がリポジトリ側の操作で変わる参照で、SHAだけが中身を一意に決めます。英語圏では SHA pinning のほか hash pinning、日本語ではハッシュ固定・コミットハッシュ指定とも呼ばれます。
タグ付け替えで起きた tj-actions/changed-files 侵害(CVE-2025-30066)
GitHub Advisory Database の GHSA-mrrh-fwg8-r2c3 によると、tj-actions/changed-files は2025年3月14日から15日にかけて侵害されました。攻撃者は複数のバージョンタグを悪意あるコミット 0e58ed8671d6b60d0890c21b07f8835ace038e67 へ遡って付け替え、ランナーのメモリから取り出したシークレットをワークフローログへ出力させました。影響は2万3,000を超えるリポジトリに及び、修正版は v46.0.1 です。
タグで参照していたワークフローは、何も変更していないのに次の実行で悪意あるコードを取り込みました。侵害前の正規コミットのSHAで固定していたワークフローは、付け替えの影響を受けていません。事件の経緯と reviewdog 側の侵害は tj-actions/changed-files侵害(CVE-2025-30066)とreviewdog/action-setup侵害の経緯で詳しく扱っています。
GitHub公式の推奨:ロックファイルを使えないリポジトリはフルコミットSHA
GitHub Docs の「Secure use reference」は、SHA固定の根拠を、差し替えには有効なGitオブジェクトでSHA-1衝突を起こす必要がある点に置いています。信頼できる作者のアクションでも、リポジトリを奪われればタグは動かされたり消されたりするとも注意しています。
2026年10月12日の改訂で、このページの推奨順位が変わりました。最上位は The best measure is to lock your workflow dependencies(ワークフローの依存をロックファイルで固定すること)になり、SHA固定は「ロックファイルを採用できないリポジトリ」向けの対策として並んでいます。SHA固定が不要になったわけではなく、ロックファイルが使えないGHESや、プレビュー機能を入れられない組織では引き続き基本の対策です。短縮SHA(7文字など)は固定とはみなされず、後述の強制設定もフルコミットSHAかどうかを検査します。
github.sha(コンテキスト)とアクションのSHA固定の違い
「github sha」で検索すると、${{ github.sha }} の情報が混ざります。これはワークフローを起動したコミットのSHAを返すコンテキストで、環境変数 GITHUB_SHA と同じ値です。アクションの参照先を固定する SHA Pinning とは別の機能で、SHA Pinningで uses: の @ の後に書くのは、呼び出すアクション側のリポジトリにあるコミットのSHAです。
github.sha の値はトリガーのイベントで変わります。push ではpushされたref(ブランチまたはタグ)の先頭コミットで、ブランチを削除したときだけは既定ブランチのSHAに戻ります。一方、pull_request では refs/pull/番号/merge のマージコミットで、PRブランチの先頭コミットとは別物です。PRの先頭コミットが欲しいときは github.event.pull_request.head.sha を使います。
# イメージのタグに使う例(pushでは先頭コミット、PRではマージコミットのSHAになる)
- run: docker build -t my-app:${{ github.sha }} .
# PRブランチの先頭コミットのSHA
- run: echo "${{ github.event.pull_request.head.sha }}"
# 短縮SHA(表示用。uses: の固定には使えない。部分文字列展開はbashの構文)
- run: echo "${GITHUB_SHA::7}"
shell: bash
SHA固定の書き方とSHAの調べ方
固定したい版のタグ(例:v7.0.1)が指すコミットのSHAを調べ、uses: の @ の後ろに40文字で書きます。人が読める版はコメントで同じ行に残します。
# 変更前:タグ参照(タグが付け替えられると中身が変わる)
- uses: actions/checkout@v7
# 変更後:フルコミットSHA+同じ行のバージョンコメント
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
SHAは GitHub CLI か git ls-remote で調べられます。gh api で commits/タグ名 を引くと、注釈付きタグでもコミットのSHAに解決されます。git ls-remote はクローン不要で使えますが、注釈付きタグではタグオブジェクトのSHAと、末尾に ^{} の付いたコミットのSHAの2行が返るので、後者を使います。
gh api repos/actions/checkout/commits/v7.0.1 --jq .sha
3d3c42e5aac5ba805825da76410c181273ba90b1
git ls-remote https://github.com/actions/checkout 'refs/tags/v7.0.1*'
3d3c42e5aac5ba805825da76410c181273ba90b1 refs/tags/v7.0.1
バージョンコメントは飾りではありません。Dependabot は同じ行のコメント(@<commit> #<tag> の形)を見て版の表記も書き換えるため、コメントが無いと更新時にどの版か追えなくなります。actions/checkout の版ごとの変更点は actions/checkoutとは|v7の既定変更・主要入力と浅いクローンの壊れ方にまとめています。
フォーク由来のSHA(impostor commit)への注意
GitHubはリポジトリとそのフォークのコミットを1つのネットワークとして扱うため、フォークにしか存在しないコミットも 元のowner/repo@SHA の形で参照できてしまいます。GitHub Docs が SHAを選ぶときは it is from the action's repository and not a repository fork を確認するよう求めているのはこのためです。
見た目では区別できないので、外部から来たPRでSHAが変わっていたら、そのSHAが本家リポジトリのタグから辿れるかを確かめます。静的解析ツール zizmor の impostor-commit 監査は、参照先リポジトリに存在しないコミットを検出します。後述のロックファイルも、ロックするコミットが参照先リポジトリのいずれかのブランチに含まれていることを条件にしています。
Enforce SHA pinning(Require actions to be pinned to a full-length commit SHA)の設定と挙動
GitHubは2025年8月15日の Changelog で、許可アクションのポリシーに2つの機能を追加しました。SHA固定の強制と、! 接頭辞による特定アクション・特定版のブロックです。どちらも同じ「Actions permissions」の画面で設定します。
設定場所:リポジトリ・Organization・Enterpriseの3階層
| 適用範囲 | 画面の場所 | REST API |
|---|---|---|
| リポジトリ | Settings → Actions → General | PUT /repos/{owner}/{repo}/actions/permissions |
| Organization | Settings → Actions → General | PUT /orgs/{org}/actions/permissions |
| Enterprise(GHEC・GHES) | Policies → Actions | 画面から設定 |
チェックボックス「Require actions to be pinned to a full-length commit SHA」は、Actionsを無効にしている場合を除き、許可範囲の各選択肢の下に表示されます。GHESでは3.19以降で使えます。OrganizationやEnterpriseが上位のポリシーを設定していると、リポジトリ側ではこの設定を変えられない場合があります。
多数のリポジトリへ段階的に入れるなら REST API の sha_pinning_required を使います。許可範囲の allowed_actions を意図せず変えないよう、先にGETで現在値を取り、同じ値を渡します。
# 現在の設定(Actionsの有効・無効と許可範囲 all / local_only / selected)を取得
EN=$(gh api repos/OWNER/REPO/actions/permissions --jq .enabled)
CUR=$(gh api repos/OWNER/REPO/actions/permissions --jq .allowed_actions)
# Actionsが有効なリポジトリだけ、許可範囲を変えずにSHA固定の強制を有効にする
if [ "$EN" = "true" ]; then
gh api -X PUT repos/OWNER/REPO/actions/permissions \
-F enabled=true -f allowed_actions="$CUR" -F sha_pinning_required=true
fi
違反時の挙動:ジョブ準備段階での失敗とマージ可否
Changelog は、固定されていないアクションを使おうとしたワークフローは失敗すると書いています。検査はワークフローの実行時に行われ、PRの作成そのものは止まりません。pinact 作者の検証記事(2025年8月)では、ジョブの「Set up job」で次のエラーが出て止まることが確認されています。
Error: The action actions/[email protected] is not allowed in szksh-lab-2/test-enforce-sha because all actions must be pinned to a full-length commit SHA.
マージまで止めたいなら、変更したワークフローがPRで実行され、そのジョブがブランチ保護の必須ステータスチェックに入っている必要があります。main へのpushや定期実行でしか動かないワークフローは、マージ後に初めて失敗します。PRの段階で確実に落とすには、後述の pinact run --check を必須チェックとして設定します。
対象範囲:Reusable Workflowとローカルアクションは対象外、ブランチ参照とcomposite内部は対象
公式ドキュメントは、強制の対象を「Organizationのアクションも GitHub 製のアクションも含むすべてのアクション」とし、Reusable workflows can still be referenced by tag. と明記しています。Reusable Workflowsの呼び出しはタグのままでも止まりません。同じリポジトリ内のローカルアクション(./ と $/)も許可側に分類されています。GHESでは3.19.9のリリースノートで、ローカルアクションをSHA固定の検査から除外する修正が記録されているため、3.19.9以降かを確認してください。
見落としやすいのは2つです。1つは社内アクションの @main のようなブランチ参照で、自社製でもSHAでなければ止まります。2026年に約350リポジトリへ強制を展開した Semgrepの報告では、タグだけを固定するツールを流した後にブランチ参照でCIを壊しています。もう1つは呼び出したアクションの内側で、Composite Actionの中で actions/setup-node@v4 のようにタグ参照していると、自分のワークフローがSHA固定済みでも実行が失敗します。対処は、依存先の修正を待つ、フォークして固定する、GitHub.comなら後述のロックファイルで内側の依存ごと固定する、のいずれかです。強制前の棚卸しは「直接のタグ参照・直接のブランチ参照・依存先の未固定」の3種類で数えると漏れません。
「!」によるブロック指定との違い
ブロック指定は、許可リストの要素に ! を付けて特定のアクションや版を拒否する機能です。*, !space-org/action@* と書くと、すべてを許可したうえで space-org/action だけを止められます。侵害が公表されたアクションを組織全体で即座に止める用途で、要素は最大1,000件まで書けます。
pinactで既存ワークフローを一括固定する手順(v5.0.0)
pinact は suzuki-shunsuke 氏が開発する、GitHub ActionsとReusable WorkflowをSHAで固定するCLIです。2026年10月19日時点の最新版は9月12日公開の v5.0.0 です。タグ参照をSHA+バージョンコメントへ書き換えるほか、書き換えずに検査する、最新版へ更新する、コメントとSHAの一致を確かめる、といった機能を持ちます。書き換えるのは自分のリポジトリのファイルだけなので、依存先アクションの内側の未固定は直せません。
pinactのインストールとworkflow・action.yamlの一括固定
brew install pinact
# APIのレート上限を引き上げるため、実行前にトークンを設定
export GITHUB_TOKEN=$(gh auth token)
# .github/workflows 配下と action.yaml を書き換える
pinact run
pinact run は actions/checkout@v7 を actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 のように、タグが指す最新パッチ版のSHAとコメントへ置き換えます。Reusable Workflowの uses: も同じ形で固定します。./ のローカル参照と docker:// の参照は書き換え対象になりません。
CIでの検査と終了コード
PRで未固定を落とすには、書き換えずに検査する --check(--fix=false の別名)を使います。
pinact run --check # 未固定があれば終了コード1
pinact run --fix=false --no-api # APIを呼ばない検査。40文字SHAでもバージョンコメントが無いと終了コード2
pinact run --check --verify-comment # 書き換えずに、コメントがSHAの実際の版と一致するかも確認
| 終了コード | 意味 |
|---|---|
| 0 | すべて固定済み、または pinact が修正した |
| 1 | 検査モードで未固定が見つかった |
| 2 | 自動修正できない(ブランチ参照・コメント不一致・min-age違反など) |
| 3 | GitHub APIのエラーやフラグの組み合わせ不正 |
CIでは0以外をすべて失敗として扱います。3は pinact 自体の失敗ですが、成功扱いにすると検査できていない変更が通ってしまいます。バージョンコメントは人でも攻撃者でも自由に書けるので、--check --verify-comment でSHAの実際の版と照合しておくと、コメントだけ新しく見せかける改ざんに気づけます。GitHub Actions上で動かすなら pinact 作者が提供する pinact-action があり(安定版は v3.0.0。v3.0.1 はプレリリース)、fix: "false" と skip_push: "true" で検査だけを行えます。
.pinact.yamlでの除外とリリース直後の版の保留
社内アクションをブランチ参照のまま使いたい場合や、公開直後の版を避けたい場合は設定ファイルに書きます。pinact init でひな形を作れます。
version: 3
min_age:
value: 7 # 公開から7日未満の版には更新しない
rules:
- ignore: true # 自社アクションの main 参照だけを固定対象から外す
conditions:
- expr: |
ActionRepoOwner == "my-org" && ActionVersion == "main"
pinact run --update で更新するとき、min_age を満たすリリースだけが候補になります。公開後まもなく見つかる侵害版を掴む機会は減らせますが、判定はリリースの公開日時やコミット日時によるもので、既存タグの付け替えを防ぐ保証にはなりません。除外はGitHub側の強制設定には無い機能です。社内アクションをブランチ参照で使い続ける組織が、強制設定ではなく pinact の検査を必須チェックにして運用する理由になっています。
固定したSHAを更新し続ける設定(Dependabot・Renovate)
SHAで固定すると、アクションの修正版は自動では入りません。更新を Dependabot のバージョン更新に任せます。
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 7
Dependabot は actions/checkout@<commit> # v7.0.1 の形を認識し、新しい版が出るとSHAとコメントの両方を書き換えるPRを作ります。コミットがどのタグにも紐づいていない場合は、最新リリースではなく最新コミットへ更新されます。Reusable Workflowの呼び出し参照も更新対象です。./.github/actions/foo のようなローカル参照と、docker:// によるコンテナの参照は更新対象外です。
cooldown は公開から一定日数たった版だけを更新候補にする設定で、GitHub Actionsのエコシステムでは default-days が使えます(メジャー・マイナー・パッチ別の日数は非対応)。GitHub.comでは cooldown を書かなくても既定で3日の猶予がかかります。セキュリティ更新には効きません。Dependabot の設定全体と自動マージは GitHub ActionsとDependabotの連携設定で扱っています。
SHA固定したアクションのDependabotアラート非対応
見落とされやすいのがアラートの扱いです。GitHub Docs には Dependabot only creates alerts for vulnerable actions that use semantic versioning and will not create alerts for actions pinned to SHA values. とあります。脆弱性が公表されても、SHA固定したアクションについてはアラートもセキュリティ更新PRも作られません。
更新PRを放置すると、修正版が出ても古いSHAのまま動き続け、アラートでも気づけません。SHA固定を始める前に、更新PRを誰がどの頻度で確認するか、脆弱性が公表されたときに誰が手動で版を上げるかを決めておきます。
Renovateで更新する場合の設定
Semgrep は上記の展開で、Helm チャートの更新にも使えることを理由に Renovate を選んでいます。Renovateのプリセット helpers:pinGitHubActionDigests は、アクションとReusable Workflowの参照に pinDigests: true を当て、タグ参照をSHAへ固定するPRを作ります。
{
"extends": ["config:recommended", "helpers:pinGitHubActionDigests"]
}
Renovate の packageRules や更新の頻度の決め方は、RenovateでTerraformを自動更新する設定|lockファイルとrangeStrategyの実際の挙動でも扱っています。
ワークフロー依存のロックファイル(actions.lock)とSHA固定の関係
GitHubは2026年10月14日の Changelog で、ワークフロー依存のロックファイルを Enterprise Cloud(データ所在地付きを含む)・Team・Free プランの公開プレビューとして提供しました。ワークフローには @v7.0.1 のような読みやすい参照を残したまま、実行するコミットを .github/workflows/actions.lock に記録したものへ固定する仕組みです。全ワークフローでロックファイルの利用を必須にする「Require lockfile」ポリシーも同時に公開プレビューとなりました。
ロックファイルが記録する内容と対象範囲
公式ドキュメントによると、ロックファイルには参照(ref)、実際に実行するコミット(commit)、参照先リポジトリの数値ID(owner_id・repo_id)が記録されます。数値IDは、名前の裏にあるリポジトリが改名や移管で入れ替わっていないかを、実行時に確かめるために使われます。
SHA固定との最大の違いは推移的な依存です。ワークフローが呼ぶ composite action の内側の uses: もロック対象になり、強制設定で止まっていた「依存先の未固定」をこちら側で固定できます。例外は Reusable Workflow の内側の依存で、Reusable Workflow を定義したリポジトリのロックファイルが受け持ちます。このため公式は、共有ワークフローを持つリポジトリから先に導入するよう勧めています。
gh actions-lockでの生成手順とワークフローの書き換え
ロックファイルは GitHub CLI の拡張 gh-actions-lock(2026年10月12日公開の v0.2.0)で生成します。手で編集してはいけません。
gh extension install github/gh-actions-lock
# .github/workflows 配下の全ワークフローをロックする
gh actions-lock
# CIで使う読み取り専用の検査(ロックが古ければ失敗する)
gh actions-lock --verify
# pre-commit 用の高速な検査(オフライン・認証不要。エントリの有無だけを見る)
gh actions-lock --verify-local --no-fix
生成時にはワークフローファイル側も書き換わります。actions/upload-pages-artifact@v4 は @v4.0.0 のような完全な版へ絞り込まれ、ロックファイルには内側で使われている actions/upload-artifact のコミットも記録されます。pinact 形式の @3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 も @v7.0.1 へ書き換わり、SHAはワークフローからロックファイルへ移ります。同じリポジトリ内の ./ 参照は、実行中のコミットに解決される $/ 形式へ置き換えられます。上流でタグが動いた場合は gh actions-lock --relock で取り直しますが、ロックしたコミットが上流から消えていると --accept-moved を付けるまで受け入れません。
強制設定・pinact・許可リストと併用するときの注意
強制設定はそのまま残せます。ロックファイルでカバーされた依存は、ワークフローファイルの表記に関係なく、ロックしたSHAで「Require actions to be pinned to a full-length commit SHA」を満たします。ロックファイルに無い依存は従来どおりフルコミットSHAが必要なので、移行中もSHA固定の強制は効き続けます。
一方で、ロックしたワークフローに pinact run --check をかけると、@v7.0.1 がタグ参照と判定されて終了コード1になります。pinact の検査を必須チェックにしているリポジトリでは、ロック済みのワークフローを .pinact.yaml の files(検査対象のファイル)から外すか、検査を gh actions-lock --verify に置き換えてから導入します。許可リストも同様で、ロック済みの依存はSHAで照合されるため、actions/checkout@v4 のようなタグ指定の行は一致しなくなります。actions/checkout@* のようなワイルドカードかフルSHAへ書き換えます。
SHA固定とロックファイルの使い分け
| 観点 | SHA固定+強制設定 | pinactの検査 | ロックファイル |
|---|---|---|---|
| 依存先の内側 | 未固定なら実行が止まる | 見ない | ロック対象 |
| 検出のタイミング | 実行時(Set up job) | PRのCI | 実行時+–verify でPR時 |
| アクション単位の除外 | 不可 | .pinact.yaml | ロックしないワークフローは対象外 |
| 段階導入の単位 | リポジトリ単位 | ファイル・アクション単位 | ワークフロー単位 |
| 対応環境 | GitHub.com・GHES 3.19以降 | どこでも | GitHub.com・GHE.com |
| 提供状態 | GA | v5.0.0 | 公開プレビュー |
GHESを使っている組織、プレビュー機能を本番のCIに入れられない組織は、これまでどおりSHA固定を基本にします。gh-actions-lock は README で pre-1.0 と明記し、ロックファイルの形式やフラグが予告なく変わり得るとしています。GitHub.com の組織なら、まず共有ワークフローを持つリポジトリで試し、依存先の未固定で強制設定が止まっていたワークフローから移すのが効果の大きい順です。「Require lockfile」ポリシーの評価モード(Evaluate)は Enterprise でしか使えないため、Team・Free プランでは対象リポジトリを絞って有効にします。なお Changelog は Locking does not determine whether the dependency itself is safe とも書いており、ロックは「レビューしたコードが動く」ことを保証するだけで、ロックする前のレビューは省けません。
導入順序:強制設定を最初に入れない理由
いきなりOrganizationで強制設定を有効にすると、未固定のワークフローが一斉に「Set up job」で止まります。しかも強制設定には、特定のアクションを除外する指定も、警告だけ出す段階もありません。次の順で進めると止まる範囲を管理できます。
pinact run --checkを手元や任意のCIで実行し、既存の未固定を洗い出す(ブランチ参照も数える)pinact runで既存ワークフローを一括固定し、同じPRでpinact run --checkを必須チェックにする(リポジトリが多ければ一括PR)- Dependabot か Renovate の更新を全リポジトリで有効にする
- 依存先の composite action の内側に未固定の参照が無いか、リポジトリ単位で強制設定を試して確かめる
- 問題が出なかったら Organization で強制設定を有効にする
4で依存先の未固定に当たったリポジトリは、GitHub.comならロックファイルへ移す候補です。社内アクションをブランチ参照で使い続ける必要がある組織では、5には進まず、pinact の除外ルール付き検査を必須ワークフローとして全リポジトリで回すほうが現実的です。CI/CDの設計や移行を外部と進める場合は、DevOps・CI/CD導入支援のように、ワークフローの棚卸しから段階展開までを1つの計画にまとめると、途中で止まるリポジトリを減らせます。
SHA固定で防げないもの
SHA固定が保証するのは「uses: に書いたアクションのコードが変わらない」ことだけです。次の経路は固定しても残ります。
- 固定したアクションの内側がタグ参照している別アクション(composite actionの入れ子)。強制設定では実行が止まる形で表に出て、ロックファイルなら固定できます
- Dockerコンテナアクションが
latestなどのイメージタグを参照している場合。アクションのSHAを固定しても、イメージは差し替えられます - アクションが実行時に
curlなどで取得するスクリプトやバイナリ。tj-actions の悪意あるコミットも、実行時に外部からスクリプトを取得して実行していました - 最初から悪意あるコミットや、フォーク由来のSHAに固定してしまった場合
- ワークフローの権限設計。
GITHUB_TOKENの書き込み権限が広いままだと、侵害されたアクションの被害も広がります
アクションの作者側でできる対策が、2025年10月28日にGAとなった GitHub Immutable Releases です。公開したリリースに紐づくタグは動かせず消せなくなりますが、actions/checkout の v7 のように付け替えるメジャータグはリリースを持たないため、@v7 で参照する限り不変にはなりません。利用者側の固定は引き続き必要です。権限の絞り方は GitHub Actionsのpermissions|GITHUB_TOKENの既定権限と最小権限の設計、ワークフローの静的解析は actionlintとは?GitHub Actionsワークフローの静的解析ツールの使い方、開発工程全体での位置づけは ソフトウェアサプライチェーンセキュリティとは?を参照してください。
SHA Pinningに関するよくある質問
SHA Pinningとは何ですか?
GitHub Actionsのワークフローで、uses: に書くアクションの参照先をタグやブランチではなく40文字のフルコミットSHAにする対策です。タグは後から別のコミットへ付け替えられますが、SHAは中身そのものを指すため変わりません。2025年3月の tj-actions/changed-files 侵害は、このタグの付け替えを突いたものでした。版がわかるように # v7.0.1 のようなコメントを同じ行に残すのが一般的です。
Require actions to be pinned to a full-length commit SHAはどこで設定しますか?
リポジトリまたはOrganizationの Settings → Actions → General にある「Actions permissions」で、許可範囲の選択肢の下に表示されるチェックボックスです。GHEC・GHESではEnterpriseの Policies → Actions でも設定でき、GHESは3.19以降が対象です。上位でポリシーが設定されていると、リポジトリ側では変更できない場合があります。REST API では PUT /repos/{owner}/{repo}/actions/permissions の sha_pinning_required で切り替えられます。
強制設定を有効にするとReusable Workflowも固定が必要ですか?
呼び出し参照は必要ありません。GitHubの公式ドキュメントに Reusable workflows can still be referenced by tag. とあり、Reusable Workflowはタグ参照のまま実行できます。同じリポジトリ内のローカルアクション(./)も許可されます。ただし Reusable Workflow の中のステップが使うアクションは「すべてのアクション」の側に入り、SHAでなければ止まります。サードパーティの composite action の内側も同じなので、リポジトリ単位で試してから広げてください。
pinactの使い方は?
brew install pinact で入れ、リポジトリのルートで pinact run を実行すると、.github/workflows と action.yaml のタグ参照がSHA+バージョンコメントへ書き換わります。CIでは pinact run --check を使うと、書き換えずに検査して未固定があれば終了コード1を返します。更新は pinact run --update、公開直後の版を避けるなら --min-age 7 を付けます。2026年10月時点の最新版は v5.0.0 です。ロックファイルを導入したワークフローはタグ表記に戻るため、pinact の検査から外します。
SHA固定するとDependabotで更新できなくなりますか?
更新はできます。package-ecosystem: "github-actions" を設定すると、Dependabot は同じ行のバージョンコメントも含めてSHAを新しい版へ書き換えるPRを作ります。ロックファイルを導入したワークフローでは、同じPRでロックファイルのエントリも作り直されます。一方で、SHA固定したアクションには Dependabot のセキュリティアラートが作られません。公式ドキュメントがそう明記しているため、SHA固定するならバージョン更新のPRを定期的に処理する運用が前提になります。
関連記事
- GitHub ActionsとDependabotの連携設定|依存関係の自動更新から自動マージまで
- GitHub Immutable Releasesとは?Enable release immutabilityの設定手順とgh release verifyでの検証
- tj-actions/changed-files侵害(CVE-2025-30066)とreviewdog/action-setup侵害の経緯・影響確認・再発防止
- GitHub Actionsのpermissions|GITHUB_TOKENの既定権限と最小権限の設計
- actionlintとは?GitHub Actionsワークフローの静的解析ツールの使い方・インストール・設定