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-token | OIDCトークンのリクエスト(writeまたはnoneのみ) |
issues | Issueとコメント |
packages | コンテナレジストリを含むGitHub Packages |
pages | GitHub Pagesのビルド |
pull-requests | プルリクエスト、ラベル、レビュー |
security-events | コードスキャンのアラートとSARIFのアップロード |
statuses | コミットステータス |
vulnerability-alerts | Dependabotアラート(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のシークレット:漏えいの経路と防ぎ方をご覧ください。デモを予約して、自社のビルドの記録をご確認ください。
