GitHub Actionsのすべてのジョブにはトークンが付与されます。GitHubは各ジョブの開始時に、ワークフローが置かれたリポジトリにスコープを限定した新しいGITHUB_TOKENを作成し、ジョブ内のどのステップもそれを使用できます。このトークンで何ができるかは、多くのチームが一度も確認したことのない設定によって決まり、古いリポジトリではその答えがいまだに「ほぼすべてに書き込める」です。

これは、ジョブ内の何かが悪意あるものに変わったときに問題になります。2025年8月のNx「s1ngularity」侵害では、リポジトリのトークンにまだ読み取りと書き込みの権限がありました。pull_request_targetワークフローへのインジェクションがこのトークンを使って公開用ワークフローを起動し、そこからNxの悪意あるバージョンの公開に使われたnpmトークンが漏えいしました。トークンが読み取り専用であれば、この段階で止められていたはずです。

この記事では、トークンの権限の設定方法、一般的なタスクごとに必要な権限、そしてワークフローに必要な最小限の権限を見つける方法を解説します。

GITHUB_TOKENとは

GITHUB_TOKENは、GitHub Actionsアプリのインストールアクセストークンです。見落とされやすい性質がいくつかあります。

  • 使うかどうかにかかわらず存在します。 ワークフローではsecrets.GITHUB_TOKENとして参照しますが、どのアクションも、渡されていなくてもgithub.tokenコンテキストから読み取れます。
  • デフォルトでディスクに書き込まれます。 actions/checkoutは、persist-credentials: falseを設定しない限り、後続のgitコマンドのためにトークンを保存します。v6より前は.git/configに書き込まれていましたが、v6で $RUNNER_TEMP配下の別ファイルに移されました。2024年、Unit 42のArtiPACKEDの調査では、.git/configを含む成果物を通じてトークンが漏えいしていることが判明しました。
  • ジョブとともに失効します。 GitHubホステッドランナーでは、ジョブの時間制限である最長6時間です。セルフホストランナーでは、最長24時間まで更新できます。

トークンは常に存在するため、制御できるのはその権限の範囲だけです。

デフォルト:permissiveとrestricted

エンタープライズ、組織、リポジトリのそれぞれに、トークンのデフォルト設定があります。

  • Permissive: すべての権限で読み取りと書き込み。
  • Restricted: contentsとpackagesのみ読み取り専用で、それ以外はなし。

2023年2月2日以降、新しいエンタープライズ、組織、リポジトリのデフォルトはrestrictedになっています。この変更は既存のものには適用されなかったため、それ以前に作成されたリポジトリや組織は、誰かが変更しない限りpermissiveのデフォルトのままです。上位のレベルでrestrictedを選ぶと、下位のレベルではpermissiveを選べません。

Settings → Actions → General → Workflow permissionsで、まず組織レベルから確認してください。同じセクションにあるAllow GitHub Actions to create and approve pull requestsは、ワークフローが必要としない限りオフのままにしておくべきです。

permissionsキー

ワークフローのpermissionsキーは、トークンの権限をデフォルトから調整します。2つのレベルで機能します。

permissions:
  contents: read

jobs:
  release:
    permissions:
      contents: write
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@<full-commit-sha>

ルールは次のとおりです。

  • ジョブレベルはワークフローレベルより優先されます。 ワークフローのデフォルトを読み取り専用にし、必要なジョブでのみ権限を引き上げます。
  • 記載しなかったものはすべてnoneになります。 1つでも権限を設定すると、それ以外はすべて削除されます。permissions: {}はすべてを削除します。
  • 値はread、write、noneのいずれかです。 writeはreadを含みます。read-allとwrite-allはすべてを一度に設定します。write-allは避けてください。

2026年9月時点で利用できる権限は次のとおりです。

権限制御対象
actionsワークフローの実行、成果物、キャッシュ
artifact-metadata成果物のメタデータレコード(2026年にこれらのAPIについてcontentsに代わったもの)
attestationsアーティファクト構成証明
checksチェック実行とチェックスイート
code-qualityカバレッジレポートなどのコード品質の結果
contentsリポジトリの内容、コミット、ブランチ、タグ、リリース
deploymentsデプロイメント
discussionsディスカッション
id-tokenOIDCトークンのリクエスト(writeまたはnoneのみ)
issuesIssueとコメント
packagesコンテナレジストリを含むGitHub Packages
pagesGitHub Pagesのビルド
pull-requestsプルリクエスト、ラベル、レビュー
security-eventsコードスキャンのアラートとSARIFのアップロード
statusesコミットステータス
vulnerability-alertsDependabotアラート(readまたはnoneのみ)

ジョブに必要な権限が欠けていると、API呼び出しは403 Resource not accessible by integrationで失敗します。このエラーは、write-allに切り替えるのではなく、そのステップに必要な権限を1つ追加すべきだというサインです。

一般的なタスクに必要な権限

タスク権限
コードのチェックアウトcontents: read
リリースの作成またはタグのプッシュcontents: write
Issueやプルリクエストへのコメントissues: write(プルリクエストへのコメントはIssues APIを経由します)
プルリクエストへのラベルやレビューの追加pull-requests: write
OIDCによるクラウドプロバイダーへの認証id-token: write
アーティファクト構成証明の作成id-token: write、attestations: write、contents: read、コンテナイメージの場合はpackages: writeも
GitHub Container Registryへのイメージのプッシュpackages: write
コードスキャンへのSARIFのアップロードsecurity-events: write
ワークフローからのGitHub Pagesのデプロイpages: write、id-token: write

ほとんどのテストジョブとビルドジョブには、contents: read以外は何も必要ありません。

フォーク、pull_request_target、Dependabot

  • フォークからのプルリクエストには、管理者が「Send write tokens to workflows from pull requests」を有効にしない限り、ワークフローが何を要求しても、pull_requestイベントで読み取り専用のトークンが渡されます。この設定はオフのままにしてください。
  • **pull_request_target**には、パブリックフォークからのプルリクエストであっても、読み取りと書き込みのトークンが渡されます。これが、よく知られたトークン窃取の多くの背景にある理由です。Nxの侵害、2024年12月のUltralyticsの侵害、2026年3月のTrivyアクションの侵害は、いずれもpull_request_targetワークフローから始まりました。GitHubはこれを絞り込みつつあります。2026年7月以降、actions/checkoutはデフォルトでこれらのワークフローでのフォークのコードのチェックアウトを拒否し、2026年11月2日からは、独自のポリシーを持たないパブリックリポジトリでpull_request_targetがデフォルトで無効になります。
  • Dependabotのプルリクエストはフォークと同様に扱われ、読み取り専用のトークンが渡され、Actionsのシークレットは渡されません。他のアップデートツールを検討している場合は、Dependabotの代替ツールをご覧ください。

最小限の権限を見つける方法

すべてのアクションのソースを読んで割り出す必要はありません。

  • GitHub Security Labのactions-permissionsは、ワークフローがトークンをどう使うかを監視し、最小限の権限を推奨します。パブリックベータ版です。
  • OpenSSF ScorecardのToken-Permissionsチェックは、トップレベルで読み取り専用の権限を設定していないワークフローを指摘します。Scorecardはこのチェックのリスクを「高」と評価しています。
  • zizmorはワークフロー向けの静的解析ツールで、過剰な権限やデフォルトの権限、認証情報を保存するチェックアウトを指摘します。
  • StepSecurityのSecure-RepoとHarden-Runnerは、アクションのナレッジベースと、ジョブが行うAPI呼び出しから、ジョブレベルの権限を推奨します。

実践的な導入手順は次のとおりです。組織のデフォルトをrestrictedに設定し、すべてのワークフローの先頭にpermissions: contents: readを追加して、ワークフローを実行します。Resource not accessible by integrationで失敗したものが、どのジョブにどの追加権限が必要かを正確に教えてくれます。

CRACIの役割

最小権限は、盗まれたトークンでできることを制限します。しかし、盗まれること自体は防げませんし、npmトークンやクラウドの認証情報など、ジョブ内の他のシークレットも同じように危険にさらされています。

CRACIは、GitHub ActionsのジョブをCRACI独自のランナーで実行します。runs-onをcraciに変更するだけです。トークンを発行するのは引き続きGitHubであるため、GITHUB_TOKEN、permissionsキー、リポジトリの設定はこれまでどおり機能します。CRACIが加えるのはランナー上の機能です。ジョブが到達できるホストを決め、ジョブの開始前に検証され、フェイルクローズで動作するエグレスポリシーと、ジョブが取得したすべてのパッケージの記録およびネットワーク接続のトレースです。ジョブ内の悪意あるコードがトークンを手に入れたとしても、ポリシーがその送信先を制限し、記録が何が起きたかを示します。

ジョブのその他のシークレットについては、GitHub Actionsのシークレット:漏えいの経路と防ぎ方をご覧ください。デモを予約して、自社のビルドの記録をご確認ください。