Chaque job GitHub Actions reçoit un token. GitHub crée un nouveau GITHUB_TOKEN au début de chaque job, limité au dépôt qui contient le workflow, et n'importe quelle étape du job peut l'utiliser. Ce qu'il peut faire dépend de réglages que beaucoup d'équipes n'ont jamais regardés et, sur les dépôts anciens, la réponse reste « écrire presque partout ».
Cela compte quand quelque chose dans le job devient malveillant. Lors de la compromission « s1ngularity » de Nx en août 2025, le token du dépôt avait encore des droits de lecture et d'écriture. Une injection dans un workflow pull_request_target s'en est servie pour déclencher le workflow de publication, qui a divulgué le token npm utilisé pour publier des versions malveillantes de Nx. Un token en lecture seule aurait arrêté cette étape.
Cet article explique comment sont définies les permissions du token, ce dont chaque tâche courante a besoin et comment trouver le minimum pour vos workflows.
Ce qu'est GITHUB_TOKEN
GITHUB_TOKEN est un token d'accès d'installation de l'application GitHub Actions. Quelques propriétés passent facilement inaperçues :
- Il existe que vous l'utilisiez ou non. Les workflows le référencent sous la forme
secrets.GITHUB_TOKEN, mais n'importe quelle action peut le lire via le contextegithub.tokensans qu'on le lui transmette. - Il est écrit sur le disque par défaut.
actions/checkoutconserve le token pour les commandes git suivantes, sauf si vous définissezpersist-credentials: false. Avant la v6, il allait dans.git/config; la v6 l'a déplacé dans un fichier séparé sous$RUNNER_TEMP. En 2024, la recherche ArtiPACKED de Unit 42 a découvert des tokens qui fuyaient par des artefacts contenant.git/config. - Il expire avec le job. Sur les runners hébergés par GitHub, cela signifie 6 heures au maximum, la limite de durée d'un job. Sur les runners auto-hébergés, il peut être renouvelé pendant 24 heures au maximum.
Comme le token est toujours là, la seule chose que vous contrôlez est l'étendue de ce qu'il peut faire.
La valeur par défaut : permissive ou restreinte
Chaque entreprise, organisation et dépôt possède une valeur par défaut pour le token :
- Permissive : lecture et écriture sur toutes les permissions.
- Restreinte : lecture seule sur
contentsetpackages, rien d'autre.
Depuis le 2 février 2023, les nouvelles entreprises, organisations et les nouveaux dépôts sont restreints par défaut. Le changement n'a pas touché les existants : tout dépôt ou toute organisation créé avant cette date conserve la valeur permissive à moins que quelqu'un ne l'ait changée. Si un niveau supérieur choisit la valeur restreinte, les niveaux inférieurs ne peuvent pas choisir la valeur permissive.
Vérifiez-la sous Settings → Actions → General → Workflow permissions, d'abord au niveau de l'organisation. La même section contient Allow GitHub Actions to create and approve pull requests, qui doit rester désactivé sauf si un workflow en a besoin.
La clé permissions
La clé permissions d'un workflow ajuste le token par rapport à la valeur par défaut. Elle fonctionne à deux niveaux :
permissions:
contents: read
jobs:
release:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<full-commit-sha>
Les règles :
- Le niveau job prime sur le niveau workflow. Définissez la valeur par défaut du workflow en lecture seule et n'augmentez les permissions que dans les jobs qui en ont besoin.
- Tout ce que vous ne listez pas devient
none. Dès que vous définissez une permission, toutes les autres sont retirées.permissions: {}les retire toutes. - Les valeurs sont
read,writeounone.writeinclutread.read-alletwrite-alldéfinissent tout d'un coup ; évitezwrite-all.
Les permissions disponibles, en septembre 2026 :
| Permission | Contrôle |
|---|---|
actions | Exécutions de workflows, artefacts et caches |
artifact-metadata | Enregistrements de métadonnées d'artefacts (a remplacé contents pour ces API en 2026) |
attestations | Attestations d'artefacts |
checks | Check runs et check suites |
code-quality | Résultats de qualité du code, comme les rapports de couverture |
contents | Contenu du dépôt, commits, branches, tags et releases |
deployments | Déploiements |
discussions | Discussions |
id-token | Demande d'un token OIDC (write ou none uniquement) |
issues | Issues et commentaires |
packages | GitHub Packages, y compris le registre de conteneurs |
pages | Builds GitHub Pages |
pull-requests | Pull requests, labels et revues |
security-events | Alertes de code scanning et envois SARIF |
statuses | Statuts de commit |
vulnerability-alerts | Alertes Dependabot (read ou none uniquement) |
Quand il manque à un job une permission nécessaire, l'appel d'API échoue avec 403 Resource not accessible by integration. Cette erreur est le signal qu'il faut ajouter la seule permission dont l'étape a besoin, et non passer à write-all.
Ce dont les tâches courantes ont besoin
| Tâche | Permissions |
|---|---|
| Récupérer le code | contents: read |
| Créer une release ou pousser un tag | contents: write |
| Commenter une issue ou une pull request | issues: write (les commentaires de pull request passent par l'API issues) |
| Ajouter des labels ou des revues à une pull request | pull-requests: write |
| S'authentifier auprès d'un fournisseur cloud avec OIDC | id-token: write |
| Créer des attestations d'artefacts | id-token: write, attestations: write, contents: read, plus packages: write pour les images de conteneurs |
| Pousser une image vers GitHub Container Registry | packages: write |
| Envoyer du SARIF vers le code scanning | security-events: write |
| Déployer GitHub Pages depuis un workflow | pages: write, id-token: write |
La plupart des jobs de test et de build n'ont besoin de rien d'autre que contents: read.
Forks, pull_request_target et Dependabot
- Les pull requests provenant de forks reçoivent un token en lecture seule sur l'événement
pull_request, quoi que demande le workflow, sauf si un administrateur active « Send write tokens to workflows from pull requests ». Laissez cette option désactivée. pull_request_targetreçoit un token en lecture et écriture, même pour les pull requests provenant de forks publics. C'est pourquoi il se trouve derrière bon nombre des vols de tokens les plus connus : la compromission de Nx, celle d'Ultralytics en décembre 2024 et celle de l'action Trivy en mars 2026 ont toutes commencé par un workflowpull_request_target. GitHub le restreint : depuis juillet 2026,actions/checkoutrefuse par défaut de récupérer le code d'un fork dans ces workflows et, à partir du 2 novembre 2026,pull_request_targetsera désactivé par défaut dans les dépôts publics sans politique propre.- Les pull requests Dependabot sont traitées comme des forks : un token en lecture seule et aucun secret Actions. Si vous envisagez d'autres outils de mise à jour, consultez les alternatives à Dependabot.
Comment trouver les permissions minimales
Vous n'avez pas à le déterminer en lisant le code source de chaque action :
- actions-permissions de GitHub Security Lab surveille la façon dont un workflow utilise son token et recommande les permissions minimales. L'outil est en bêta publique.
- Le contrôle Token-Permissions d'OpenSSF Scorecard signale les workflows qui ne définissent pas de permissions en lecture seule au niveau supérieur. Il classe ce contrôle comme à risque élevé.
- zizmor, un analyseur statique de workflows, signale les permissions excessives ou par défaut et les checkouts qui conservent les identifiants.
- Secure-Repo et Harden-Runner de StepSecurity recommandent des permissions par job à partir d'une base de connaissances sur les actions et des appels d'API effectués par un job.
Un déploiement pratique : définissez la valeur par défaut de l'organisation sur restreinte, ajoutez permissions: contents: read en tête de chaque workflow et exécutez les workflows. Ceux qui échouent avec Resource not accessible by integration vous indiquent exactement quel job a besoin de quelle permission supplémentaire.
La place de CRACI
Le moindre privilège limite ce que peut faire un token volé. Il n'empêche pas qu'il soit volé, et les autres secrets du job, comme les tokens npm et les identifiants cloud, sont tout aussi exposés.
CRACI exécute vos jobs GitHub Actions sur ses propres runners. Vous remplacez runs-on par craci ; GITHUB_TOKEN, la clé permissions et les réglages de votre dépôt fonctionnent comme avant, car c'est toujours GitHub qui émet le token. Ce qu'ajoute CRACI se trouve sur le runner : une politique de trafic sortant qui décide quels hôtes un job peut atteindre, est validée avant le démarrage du job et bloque tout en cas d'échec, ainsi qu'une trace de chaque paquet récupéré par le job, avec une trace de ses connexions réseau. Si du code malveillant dans un job met la main sur un token, la politique limite les destinations où il peut l'envoyer, et la trace montre ce qui s'est passé.
Pour les autres secrets du job, consultez Secrets GitHub Actions : comment ils fuient et comment les verrouiller. Réservez une démo pour voir la trace de l'un de vos propres builds.
