---
title: "GitHub Actionsのpermissions｜GITHUB_TOKENの既定権限と最小権限の設計"
url: "https://www.issoh.co.jp/tech/details/16937/"
published: 2026-08-25
updated: 2026-09-27
categories: ["GitHub"]
publisher: "株式会社一創"
---

# GitHub Actionsのpermissions｜GITHUB\_TOKENの既定権限と最小権限の設計

GitHub Actionsの`permissions`は、ジョブに配られる`GITHUB_TOKEN`がリポジトリのどこまで触れるかを、ワークフローのYAML側から絞り込むためのキーです。書かなければ組織やリポジトリの設定に従った既定値がそのまま渡り、書けばその1か所で境界が決まります。ワークフローの本数が増えるほど、この1行の有無が事故の広がり方を左右しました。

ワークフローの基本構造やイベントの選び方には立ち入りません。全体像は[GitHub Actionsとは？できることとCI/CD自動化の解説](https://www.issoh.co.jp/column/details/3005/)、起動条件の設計は[GitHub Actionsのトリガー（on）の設計](https://www.issoh.co.jp/tech/details/16923/)を参照してください。本記事は`GITHUB_TOKEN`へ与える権限の決め方だけを扱います。仕様と数値は2026年8月時点の公式ドキュメントで実測しました。

## まとめ：permissionsで最初に決める三つの分岐

先に結論から書きます。第一の分岐は、そのリポジトリの既定権限がどちらになっているかです。2023年2月2日以降に作られたenterpriseとorganizationは読み取り専用が既定ですが、それ以前から動いている組織は読み書きのまま残っています。ここを確かめずに個別のワークフローだけ絞っても、書き忘れた1本が広い権限で走り続けました。

第二の分岐は、`permissions`をワークフロー直下に置くか、ジョブ直下に置くかという配置です。どちらに書いても、明示しなかったスコープはすべて`none`へ落ちます。この挙動を知らないまま1スコープだけ足すと、それまで動いていた別の処理が権限不足で止まります。逆に言えば、この仕様は最小権限を作るうえで使いやすい性質でもありました。

第三の分岐は、そもそも`GITHUB_TOKEN`で足りるのかという点です。別のリポジトリへ触る、組織を横断する、あるいは自分が起こしたpushで次のワークフローを回したい。この三つはいずれも`GITHUB_TOKEN`の設計上の範囲外で、細粒度の個人アクセストークンかGitHub Appのインストールトークンへ差し替える判断になります。

## GITHUB\_TOKENの正体｜ジョブごとに払い出される一時トークンの寿命と範囲

`GITHUB_TOKEN`は、ジョブが始まるたびにGitHubが自動で作るGitHub Appのインストールアクセストークンです。人のアカウントにも組織のシークレットにも紐づいておらず、権限の適用範囲はそのワークフローを含むリポジトリ1つに限定されています。`secrets.GITHUB_TOKEN`と`github.token`はどちらも同じ値です。

### トークンの有効期限は最大6時間｜ジョブ終了と同時に無効化される

トークンはジョブが終わった時点で無効化されます。GitHubホステッドランナーではジョブの実行時間そのものが最長6時間なので、トークンの寿命も最長で6時間です。セルフホストランナーではジョブが最長5日まで走りますが、その場合でもトークンの更新は24時間までに制限されています。長時間ジョブでAPIを叩き続けるなら、この境界を先に確かめてください。

ログ上のトークン値は伏せ字へ置換されますが、Base64へ包めばその保護は効きません。[actions/checkoutとは｜v7の既定変更と主要入力](https://www.issoh.co.jp/tech/details/16927/)で扱っている`persist-credentials`は既定が`true`で、取得後もジョブの中に資格情報が残る設計です。取得しかしないジョブなら、ここを`false`にするだけで持ち出せる面が1つ減ります。

### 既定の権限が読み取り専用かどうかを組織とリポジトリの設定で確かめる

GitHubは2023年2月2日の変更で、新しく作られるenterpriseとorganizationの既定を「Read repository contents and packages permissions」へ切り替えました。既存のenterprise・organization・リポジトリは対象外だったため、その前から運用されている組織は今も読み書きが既定のまま動いている可能性があります。

確認する場所は、リポジトリなら Settings の Actions の General にある Workflow permissions、組織なら組織側の同じ画面です。組織で選んだ値がリポジトリの上限になり、enterpriseの配下に新設されたorganizationは親の設定を引き継ぎます。受託案件では、まずこの画面で現状を控えるところから始めています。

## permissionsキーの書き方｜スコープ一覧と指定漏れがnoneへ落ちる仕様

`permissions`はマップで書き、キーにスコープ名、値に`read`・`write`・`none`のいずれかを取ります。`write`は`read`を含むため、書き込みが要るスコープに読み取りを併記する必要はありません。

### permissionsで指定できるスコープ一覧と読み書きの三つの値

2026年8月時点で指定できるスコープは16種です。`actions`、`artifact-metadata`、`attestations`、`checks`、`code-quality`、`contents`、`deployments`、`discussions`、`id-token`、`issues`、`packages`、`pages`、`pull-requests`、`security-events`、`statuses`、`vulnerability-alerts`が並びます。数年前の記事を写した設定では`artifact-metadata`と`code-quality`が抜け落ちます。

値には例外が二つあります。`id-token`は`write`か`none`しか取らず、OpenID Connectで外部クラウドへ接続するときに`write`が要ります。`vulnerability-alerts`は逆に`read`か`none`だけで、書き込みは指定できません。

### 明示したスコープ以外はすべてnoneへ落ちるという挙動と注意点

公式ドキュメントは「If you specify the access for any of these permissions, all of those that are not specified are set to none.」と書いています。つまり`permissions`を1行でも書いた瞬間、書かなかったスコープは既定値ではなく`none`になります。

```
permissions:
  contents: read
  pull-requests: write
```

この例では`contents`と`pull-requests`以外の14スコープが`none`です。PRへコメントを書くために`pull-requests: write`だけ足したところ、同じジョブのチェック更新が止まる相談を受けました。原因はたいてい`checks`や`statuses`の書き漏れで、足りない分を明示すれば戻ります。

### read-all・write-all・空マップという三つの短縮記法

スコープを1つずつ書かずに済ませる記法も三つ用意されています。`permissions: read-all`は全スコープに読み取りを、`permissions: write-all`は全スコープに書き込みを与える書き方です。`permissions: {}`は全スコープを無効にする書き方で、外部の情報を取ってくるだけのジョブや、シークレット経由で別サービスだけを叩くジョブに向いています。

```
jobs:
  lint:
    runs-on: ubuntu-latest
    permissions: {}
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false
```

ただし`permissions: {}`を置くと、プライベートリポジトリでは取得の工程そのものが失敗します。自分のコードを取ってくるには`contents: read`が要るため、空マップではなく`contents`だけを許す形が最小になります。

### ワークフロー全体とジョブ単位で書いたときの効き方と上書き順序

トップレベルに書いた`permissions`はそのワークフローの全ジョブに適用され、ジョブ直下に書いた値がそれを上書きします。ジョブごとに必要な権限が違う構成では、トップレベルで`contents: read`まで落とし、書き込みが要るジョブだけ個別に足す形が扱いやすくなりました。

```
permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5

  release:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - run: gh release create "v1.0.0"
        env:
          GH_TOKEN: ${{ github.token }}
```

ここで注意したいのは、ジョブ直下に書いた側でも「書かなかったスコープは`none`」の規則がそのまま働く点です。上の`release`ジョブはトップレベルの`contents: read`を引き継いでいるのではなく、`contents: write`だけを持ち他は`none`という状態になります。呼び出し先の再利用ワークフローへどう伝わるかは、[Reusable Workflowsとは？workflow\_callでの作成と呼び出し](https://www.issoh.co.jp/tech/details/10765/)の入出力設計と併せて確かめてください。

## 用途別に必要なスコープを引き当てる手順と段階的な絞り込みの進め方

スコープ名とAPIの対応は覚えるものではなく、引くものです。実務で頻出する組み合わせを先に手元へ置いておくと、絞り込みの往復が減ります。

### 実務でよくあるジョブと必要になるスコープの対応を一覧表で示す

| ジョブの目的          | 必要なスコープ                  |
| --------------- | ------------------------ |
| ビルドとテストだけ       | contents: read           |
| タグ付与とリリース作成     | contents: write          |
| PRへコメントやラベル     | pull-requests: write     |
| Issueの自動起票      | issues: write            |
| ghcr.ioへイメージ公開  | packages: write          |
| コード検査結果の送信      | security-events: write   |
| OIDCで外部クラウド接続   | id-token: write          |
| GitHub Pagesへ反映 | pages と id-token に write |
| 成果物の来歴を署名       | attestations: write      |
| 他のワークフロー再実行     | actions: write           |
| デプロイ状態の更新       | deployments: write       |

OpenID Connectで外部クラウドへ繋ぐ場合、`id-token: write`は署名済みトークンを要求する権限であってクラウド側の権限ではありません。どのロールを引き受けられるかは接続先の信頼ポリシーで決まり、そこには環境名も差し込めます。環境単位で権限差を作る設計は[GitHub ActionsのEnvironmentsと承認ゲート](https://www.issoh.co.jp/tech/details/16935/)にまとめました。

### 既定をwrite-allにせず実測しながら絞り込む三段階の進め方

最初から正解のスコープ表を書き切ろうとすると手が止まります。三段階で寄せていく進め方が実際には速く終わりました。

第一段階は、トップレベルに`contents: read`だけを置いて全ワークフローを一度流すことです。ここで落ちたジョブが、追加の権限を要求している箇所そのものになります。第二段階は、落ちたジョブのログからAPIの応答コードを拾い、対応するスコープをジョブ直下へ1つずつ足す作業です。403が返っていればスコープ不足、404が返っていれば対象が見えていないという切り分けになり、後者もほとんどは読み取り権限の欠落でした。

第三段階は、足した権限をジョブ単位へ降ろして、トップレベルを最小のまま固定することです。ここまで済んだら、YAMLの静的解析を挟んで崩れを防ぎます。[actionlintとは？ワークフローを静的解析するLintツール](https://www.issoh.co.jp/tech/details/10397/)のルールはスコープ名の綴りミスも拾うので、レビューの前段に置くと差し戻しが減りました。[GitHub Actionsの定期実行（cron）の実務](https://www.issoh.co.jp/tech/details/16925/)で触れている失敗通知の仕組みは、権限不足の検知にも使えます。

## GITHUB\_TOKENで足りない場面｜PATとGitHub Appトークンの選び分け

スコープを足しても解決しない要求が二つあります。ここを取り違えると、いつまでも`permissions`を書き換え続けることになりました。

### GITHUB\_TOKENでのpushが次のワークフローを起動しない制約

公式ドキュメントは、リポジトリの`GITHUB_TOKEN`を使ってpushしても、pushイベントで動くワークフローは新たに走らないと明記しています。無限ループを避けるための仕組みで、`contents: write`を与えても挙動は変わりません。生成物をコミットして次のパイプラインへ渡す構成は、この時点で成立しなくなります。

例外もあります。`workflow_dispatch`と`repository_dispatch`は常にワークフロー実行を作るため、後続を明示的に呼ぶ設計へ寄せれば回避できました。[GitHub Actionsの手動実行（workflow\_dispatch）とAPI起動](https://www.issoh.co.jp/tech/details/16931/)がその入口です。もう一つ、`GITHUB_TOKEN`が作成・更新したPRの`pull_request`イベントは、`opened`・`synchronize`・`reopened`に限って承認待ちで実行が作られます。

### 他のリポジトリや組織横断の操作でトークンを差し替える判断基準

二つ目は範囲の問題です。`GITHUB_TOKEN`はワークフローを含むリポジトリに限定されたインストールトークンなので、隣のリポジトリのIssueを立てる、共通のマニフェストリポジトリを更新するといった操作には届きません。

### 細粒度PATとGitHub Appトークンの寿命と権限の違い

差し替え先の候補は二つです。細粒度の個人アクセストークンは発行が手軽な反面、発行した個人に紐づくため、担当者の異動や退職でパイプラインが止まります。有効期限と権限の粒度は[GitHub PATとは？classic／fine-grainedの違い](https://www.issoh.co.jp/tech/details/7545/)に整理しました。

もう一方のGitHub Appは、インストール先に紐づくため個人から独立します。ワークフローの中でインストールアクセストークンを都度発行する形にすれば、長期の秘密情報をリポジトリへ置かずに済みます。発行されたトークンは1時間で失効するため、漏れたときの影響も短く抑えられました。

```
jobs:
  sync:
    runs-on: ubuntu-latest
    permissions: {}
    steps:
      - uses: actions/create-github-app-token@v3
        id: app-token
        with:
          client-id: ${{ vars.APP_CLIENT_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}
          owner: ${{ github.repository_owner }}
      - run: gh issue create --repo "other-org/other-repo" --title "sync"
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }}
```

この`create-github-app-token`は2026年3月のv3系でNode 24へ移り、5月のリリースでenterprise向けAppにも対応しました。入力は`client-id`が第一級で、従来の`app-id`は非推奨のフォールバックへ変わっています。なお`vars`と`secrets`の使い分けは[GitHub Actionsの環境変数の使い分け](https://www.issoh.co.jp/tech/details/16921/)で扱っています。

## fork PRでの権限の縮退とサプライチェーン攻撃への備え方

公開リポジトリで外部PRを受けると、権限の話は一段複雑になります。誰が書いたか分からないコードが、こちらのランナーで走るためです。

### fork由来のPRでは書き込み権限が読み取りへ落とされる仕組み

fork元のリポジトリから届いたPRでは、`pull_request_target`以外のPRイベントで、かつ Send write tokens to workflows from pull requests の設定が選ばれていない場合、書き込み権限はすべて読み取りへ落とされます。公式ドキュメントもfork向けには読み取りの増減しかできず書き込みは通常与えられないと書いており、YAML側で`write`を並べても通りません。

同じ扱いはDependabotが作るPRにも及びます。Dependabotのワークフロー実行はfork由来として動くため、読み取り専用の`GITHUB_TOKEN`になります。依存更新を自動マージしたい要件は、この制約を前提にAppトークンなどへ差し替える設計が必要でした。

### pull\_request\_targetを使うときに増える権限のリスク

`pull_request_target`は、fork由来であっても読み書きの権限が付き、シークレットも参照できるイベントです。ワークフローの定義はPRの中身ではなくベースブランチ側が使われるため、一見すると安全に見えます。危ないのはその先で、PRのブランチを明示的に取得してビルドやテストを走らせた瞬間、外部が書いたコードが強い権限のトークンとシークレットを持つジョブの中で実行されます。

このイベントを使うなら、ラベル付けやコメントのようにPRの中身を実行しない処理へ限定するのが実務上の線引きです。どうしてもビルドが要るなら、取得と実行は権限のないジョブへ分け、結果の書き戻しだけを`pull_request_target`側で行う二段構えにします。イベントごとの性質は起動条件の記事で整理しました。

### permissionsだけでは塞げない範囲とSHA固定・静的解析

`permissions`が絞るのは、あくまでそのジョブへ配られるトークンの権限です。ジョブの中で動くサードパーティ製actionは、同じ環境変数とファイルシステムを共有します。つまり参照しているactionが差し替えられた場合、絞ったトークンの範囲内であれば自由に使われる余地が残ります。

ここを詰めるには、参照先の版を動かない値へ固定する対策を重ねます。[SHA Pinningとは？コミットSHAで固定する方法](https://www.issoh.co.jp/tech/details/10238/)による版の固定と、静的解析による書式の検査、そして`persist-credentials`を`false`にする資格情報の非保持。この三つと`permissions`を合わせて、はじめて乗っ取られても持ち出せるものが少ない状態になりました。

## 受託開発の現場でpermissionsをどう決めるか｜採用条件と見送る場面

ここからは判断の話です。すべてのリポジトリで`permissions`を1行ずつ書き切る必要はないと考えています。

### permissionsを最小へ寄せる採用条件を三つの前提で示す

明示的に書き切るべきなのは、次の三つのどれかに当てはまる場合です。第一に、外部からのPRを受け付けるリポジトリであること。第二に、パッケージ公開やリリース作成など、外へ出ていく成果物をワークフローが自動で作っていること。第三に、ワークフローが5本を超えて、誰がどれを直したか追いにくくなっていること。

この三つのいずれかを満たすなら、トップレベルに`contents: read`を置き、書き込みが要るジョブだけ個別に足す形へ寄せてください。引き渡し後に第三者の目で確かめたい場合は、[脆弱性診断・セキュリティ診断](https://www.issoh.co.jp/service/system/vulnerability/)のように、権限設計とワークフロー定義まで含めて範囲に入れる形が実務的でした。

### write-allのまま運用してよい場面と、そのときの代替策

見送ってよい場面もはっきりしています。単一のプライベートリポジトリで、外部からのPRを受けず、ワークフローがビルドとテストだけで、しかも組織の既定がすでに読み取り専用になっている。この条件が揃っているなら、ワークフローごとに`permissions`を書く効果はほとんどありません。組織設定を読み取り専用にする一手で足ります。

問題は、既定が読み書きのまま残っている古い組織で、なおかつ個別のYAMLも書かれていないケースです。ここではワークフローを1本ずつ直すより、組織設定を読み取り専用へ切り替えて壊れたものだけ足し直すほうが早く終わりました。CI/CD全体をどこまで自動へ寄せるかという前提は[CI/CDとは？導入すべき企業の判断基準](https://www.issoh.co.jp/column/details/13002/)で整理しています。

## よくある質問

### permissionsを書いていないワークフローはどの権限で動きますか？

組織またはリポジトリの Workflow permissions で選ばれている既定値がそのまま渡ります。2023年2月2日以降に新設されたenterpriseとorganizationは読み取り専用が既定ですが、それ以前から存在する組織は変更の対象外だったため読み書きのまま残っていることがあります。

### 空マップのpermissionsを書くと取得の工程も失敗しますか？

プライベートリポジトリでは失敗します。自分のリポジトリを取ってくるには`contents: read`が要るためです。外部のリソースしか触らないジョブでなければ、空マップではなく`contents: read`だけを許す形が現実的な最小になります。

### fork元からのPRでシークレットは使えますか？

使えません。`pull_request_target`以外のPRイベントでは、fork由来の実行にシークレットは渡らず、書き込み権限も読み取りへ落とされます。外部PRのCIは、シークレットなしで完結するビルドとテストに範囲を限定して組んでください。

### 組織の既定を読み取り専用へ変えると既存のワークフローは壊れますか？

書き込みを使っていたワークフローは止まります。ただし止まり方は403などの明確な失敗なので、切り替えてから落ちたものへ`permissions`を足す進め方が結果的に短く済みました。事前に洗い出すなら、リリース作成・パッケージ公開・PR操作を含むものを先に確認してください。

### GITHUB\_TOKENと細粒度PATはどちらを選ぶべきですか？

同一リポジトリで完結する処理なら`GITHUB_TOKEN`が第一候補です。別リポジトリへ触る、組織を横断する、pushで後続のワークフローを起こしたい、のいずれかに当たるときだけ差し替えます。差し替え先は個人に紐づかないGitHub Appのトークンを優先しました。

## 関連記事

- [actions/checkoutとは｜v7の既定変更と主要入力](https://www.issoh.co.jp/tech/details/16927/)：資格情報を残さない設定が分かります。
- [SHA Pinningとは？コミットSHAで固定する方法](https://www.issoh.co.jp/tech/details/10238/)：参照先の版を固定する手順を確認できます。
- [actionlintとは？静的解析するLintツール](https://www.issoh.co.jp/tech/details/10397/)：スコープ名の綴りミスを機械で拾えます。
- [GitHub ActionsのEnvironmentsと承認ゲート](https://www.issoh.co.jp/tech/details/16935/)：環境単位の権限差を整理できます。
- [GitHub Actionsのトリガー（on）の設計](https://www.issoh.co.jp/tech/details/16923/)：pull\_request\_targetの前提が分かります。
- [GitHub Actionsの手動実行（workflow\_dispatch）](https://www.issoh.co.jp/tech/details/16931/)：後続を明示的に呼ぶ設計を確認できます。
- [GitHub PATとは？classic／fine-grainedの違い](https://www.issoh.co.jp/tech/details/7545/)：差し替え先の候補を比較できます。
- [CI/CDとは？導入すべき企業の判断基準](https://www.issoh.co.jp/column/details/13002/)：自動化を広げる前提を整理できます。

---

出典: [GitHub Actionsのpermissions｜GITHUB\_TOKENの既定権限と最小権限の設計](<https://www.issoh.co.jp/tech/details/16937/>)（株式会社一創）
