GitHub Actions a été largement exploité dans de nombreuses attaques de la chaîne d’approvisionnement récemment, et les mauvaises configurations de workflow ont joué un rôle majeur. Il est dangereux de s'y aventurer seul ! Prenez cette (liste de contrôle).

Pourquoi y a-t-il tant de problèmes de sécurité avec GitHub Actions ?
GitHub Actions, le système CI/CD et d'automatisation intégré de GitHub, n'a pas une sécurité intrinsèquement faible, mais il offre de nombreuses façons de se tirer une balle dans le pied.
La plateforme fonctionne comme prévu, mais les configurations par défaut sont généralement définies pour la commodité et la flexibilité, et non pour la sécurité. pull_request_target existe pour une raison, et les tags mutables sont certes pratiques. Mais ces décisions de conception ont créé une surface d'attaque qui n'est devenue évidente que plus tard.
GitHub Actions est également le système CI/CD par défaut pour la plupart des projets open source, et la prise de contrôle d'un projet open source permet aux hackers d'atteindre les victimes les plus en aval. Les workflows détiennent souvent des identifiants qui publient sur npm et PyPI, donc un workflow compromis peut pousser une version malveillante d'un package, et chaque développeur qui installe ce package l'obtient également.
Une autre raison pour laquelle nous continuons de voir tant de vecteurs similaires apparaître dans les incidents est que la surface d'attaque est bien documentée dans la recherche publique depuis des années. Les attaquants scannent parfois les dépôts à la recherche de ces mauvaises configurations pour trouver des cibles d'attaque faciles (voir prt-scan). GitHub priorise des protections plus robustes au cours de la prochaine année pour mieux protéger les utilisateurs, mais une grande partie de la responsabilité incombe toujours à l'utilisateur pour configurer GitHub de manière sécurisée et correcte.
Suivre toutes les meilleures pratiques ne rendra pas vos workflows infaillibles. Bien que vous ne puissiez pas vous protéger entièrement d'un mainteneur compromis ou d'une vulnérabilité zero-day dans l'infrastructure de GitHub, vous pouvez fermer de nombreux vecteurs que les attaquants exploitent activement ces derniers temps, et rendre vos dépôts une cible plus difficile que la plupart.
Meilleures pratiques pour sécuriser vos workflows GitHub Actions
Commencez ici. Si vous ne faites que cinq choses de cette liste de contrôle :
- Épinglez toutes les actions tierces à un SHA de commit complet
- Définir par défaut
GITHUB_TOKENpermissions en lecture seule - Ne jamais
pull_request_targetdans les dépôts publics - Ne jamais interpoler
${{ github.* }}directement dansrun :étapes - Utilisez OIDC pour les identifiants cloud au lieu de secrets à longue durée de vie
Mais nous vous recommandons de lire la liste de contrôle en détail, où nous expliquons tous nos conseils de sécurité pour GitHub Actions et pourquoi vous en avez besoin. Consultez également les outils en bas, qui vous aideront à implémenter et à appliquer ces meilleures pratiques.
Configuration du déclencheur
1. Ne jamais utiliser pull_request_target dans les dépôts publics
pull_request_target existe pour permettre aux workflows déclenchés par des PR de forks de s'exécuter avec accès aux secrets du dépôt de base. Il permet d'étiqueter ou de commenter automatiquement les PR des contributeurs externes. C'est une bonne idée, mais l'accès aux secrets rend cela dangereux.
Contrairement au standard pull request déclencheur, pull_request_target s'exécute dans le contexte du dépôt de base, quelle que soit l'origine de la PR. N'importe qui peut ouvrir une PR contre un dépôt public. Si votre workflow se déclenche sur cette PR, même si elle n'est pas merge, tous les secrets explicitement référencés dans le workflow sont chargés dans l'environnement du runner et deviennent accessibles à tout code s'exécutant dans ce runner. Le script de l'attaquant peut facilement les lire via une recherche d'environnement comme os.environ.get('MY_SECRET') et les renvoyer à un attaquant sans laisser de traces.
Si vous en avez besoin dans un dépôt privé, exigez que les PR des contributeurs débutants obtiennent une approbation d'un mainteneur avant de pouvoir déclencher des workflows. GitHub prend en charge cette fonctionnalité nativement sous Paramètres > Actions > Workflows de pull request de fork.
Dans la nature : L'attaque Trivy de mars 2026 est survenue en exploitant pull_request_target. Les ingénieurs pensaient que c'était sûr car il ne serait pas autorisé à exécuter quoi que ce soit, mais ce n'était pas suffisant. La PR a envoyé les secrets à l'attaquant et les a utilisés pour s'introduire dans leurs comptes. Peu de temps après, un attaquant a commencé à scanner GitHub spécifiquement pour les dépôts avec pull_request_target activé et a ouvert quelques centaines de PR en une journée environ.
2. Éviter workflow_run dans les dépôts publics
workflow_run permet d'enchaîner les workflows de sorte qu'un workflow en aval se déclenche lorsqu'un workflow en amont est terminé. Le problème est que le workflow en aval s'exécute avec des permissions d'écriture et un accès aux secrets, quelle que soit la cause du déclenchement du workflow en amont, y compris une PR de fork d'un contributeur externe.
Si un workflow en amont pull request workflow est vulnérable à l'injection de script, un attaquant peut empoisonner l'artefact de sortie via une PR. Le workflow en aval consomme alors du contenu contrôlé par l'attaquant dans un contexte privilégié avec accès aux secrets, de sorte que le contenu contrôlé par l'attaquant provenant d'une PR non fiable a maintenant atteint un workflow avec accès aux secrets via un saut supplémentaire. Le chemin d'attaque est plus long que pull_request_target mais mène au même résultat.
La meilleure solution est d'éviter ce modèle. Si un déploiement ne doit se produire que sur des pushes vers main, déclenchez-le directement avec un push sur main plutôt que de l'enchaîner via workflow_run. workflow_runSi vous avez vraiment besoin de github.event.workflow_run.event , vérifiez pull request avant d'entreprendre toute action privilégiée. Si le déclencheur en amont était une
jobs:
deploy:
if: github.event.workflow_run.event == 'push'
plutôt qu'un push d'un mainteneur, annulez avant de déployer ou d'écrire quoi que ce soit. signalera workflow_run les workflows qui exécutent des actions privilégiées sans la vérification d'événement si vous souhaitez une détection automatisée sur l'ensemble de vos dépôts.
3. Auditer les workflows utilisant d'autres déclencheurs privilégiés
En plus de pull_request_target et workflow_run, soyez attentif aux autres déclencheurs qui s'exécutent avec un accès aux secrets. Ceux-ci incluent issue_comment, issues, pull_request_review, et pull_request_review_comment. Parce qu'ils s'exécutent tous avec un accès aux secrets, ils peuvent être influencés par des contributeurs externes. Les mêmes règles d'injection de script s'appliquent, à savoir ne jamais interpoler les valeurs de ces événements directement dans run : les étapes. Nous en parlerons plus en détail dans la section suivante.
Gestion des entrées non fiables
1. Prévenir l'injection de scripts en considérant tous les noms de branches, titres de PR, messages de commit et corps d'issues comme des entrées non fiables
C'est le même principe que l'injection SQL. Les valeurs contrôlées par l'utilisateur qui se retrouvent dans une commande shell sont interprétées comme du code, et non comme des données. Par conséquent, n'interpolez jamais github.* les valeurs directement dans run : les étapes. La solution consiste à assigner d'abord la valeur à une variable d'environnement, puis à référencer cette variable dans le script shell :
# vulnerable
- run: echo "Branch is ${{ github.head_ref }}"
# safe
- run: echo "Branch is $BRANCH"
env:
BRANCH: ${{ github.head_ref }}
Lorsque la valeur est assignée à une variable d'environnement, le shell la lit comme une chaîne de caractères à l'exécution plutôt que de l'interpréter comme de la syntaxe au moment de l'analyse. Cela s'applique à tout ce qui provient d'une entrée contrôlée par l'utilisateur : github.head_ref, github.event.pull_request.title, github.event.issue.body, github.event.commits[0].message, et des valeurs de contexte similaires. Un bon scanner SAST comme Aikido détectera cela et fournira le correctif dans une pull request.
Dans la pratique : Dans l' attaque Ultralytics, un attaquant a nommé une branche avec une commande curl qu'un workflow a interpolée directement dans une run : étape, l'exécutant comme du code. L' attaque Nx/s1ngularity, découverte pour la première fois par Aikido Security, a combiné cela avec pull_request_target, où une PR vers une branche obsolète a déclenché un workflow vulnérable qui a divulgué un GITHUB_TOKEN avec des permissions de lecture/écriture, qui a ensuite été utilisé pour publier des packages npm malveillants.
2. Pour les agents IA dans les workflows personnalisés, utiliser des jetons en lecture seule et ne pas inclure les entrées utilisateur brutes dans les invites
Les agents IA exécutés dans les workflows GitHub Actions ont le même accès aux secrets que n'importe quelle autre étape. Si un agent traite des titres d'issues, des descriptions de PR ou des messages de commit dans le cadre de son prompt, un attaquant peut insérer des instructions dans ce texte et manipuler l'agent pour qu'il effectue des actions privilégiées, comme la modification de fichiers ou l'exfiltration de données via les outils auxquels il a accès. Puisqu'il n'existe aucun moyen d'empêcher l'injection de prompt dans les LLM, ne donnez pas aux agents IA un accès au-delà de la lecture, et ne leur permettez pas de recevoir des titres d'issues bruts, des descriptions de PR ou des messages de commit comme entrée de prompt.
Sur le terrain : Les chercheurs d'Aikido ont démontré cela avec PromptPwnd: un titre d'issue malveillant injecté dans un workflow Gemini CLI a poussé l'agent à écrire des secrets de dépôt dans un fil d'issue public en utilisant son propre gh accès aux outils.
3. Considérer les sorties générées par les LLM comme des entrées non fiables
Lorsqu'un workflow utilise un LLM pour générer une commande, un script ou un chemin de fichier et transmet cette sortie directement à une run : étape, cela crée le même risque d'injection que l'interpolation d'un nom de branche ou d'un titre de PR. La sortie d'un LLM n'est pas garantie d'être sûre, et un attaquant qui peut influencer le prompt peut influencer ce qui est exécuté. Comme nous l'avons abordé précédemment concernant l'injection de prompt, assignez d'abord la sortie du LLM à une variable d'environnement, validez-la si possible, et ne la transmettez jamais directement à une commande shell.
4. Ne jamais écrire de données non fiables dans GITHUB_ENV ou GITHUB_PATH
Écrire dans GITHUB_ENV définit des variables d'environnement pour toutes les étapes suivantes du job. Écrire dans GITHUB_PATH prépare des entrées au PATH système pour toutes les étapes suivantes. Si un contenu non fiable atteint l'un ou l'autre de ces fichiers, un attaquant peut définir des variables d'environnement arbitraires, comme NODE_OPTIONS qui déclenchent l'exécution de code. Ils pourraient également injecter un binaire malveillant au début du PATH qui serait appelé à la place d'un outil de confiance. Le schéma vulnérable est un workflow qui télécharge un artefact ou lit une entrée contrôlée par l'utilisateur et l'écrit directement dans $GITHUB_ENV sans assainissement. Traitez tout contenu écrit dans ces fichiers avec la même prudence qu'un run : étape.
Gestion des artefacts
5. Extraire les artefacts vers un répertoire temporaire comme /tmp plutôt que l'espace de travail, afin d'éviter d'écraser les fichiers de workflow
L'extraction d'un artefact directement dans l'espace de travail pourrait permettre à une archive au contenu malveillant d'écraser des fichiers de workflow, des scripts ou des outils dont dépendent les étapes ultérieures. L'extraction vers /tmp ou un autre répertoire isolé maintient le contenu des artefacts à l'écart de tout ce que votre workflow considère comme fiable. GitHub prend également en charge la vérification du digest SHA256 si vous avez besoin de garanties d'intégrité plus robustes.
6. Exclure les fichiers secrets (.env, les fichiers de configuration, les identifiants) des artefacts téléchargés et évitez les path: . modèles qui englobent tout
Le path: . modèle dans une actions/upload-artifact étape télécharge tout le contenu du répertoire de travail, ce qui peut inclure des .env fichiers, des fichiers de configuration avec des identifiants intégrés, ou des secrets mis en cache écrits sur le disque par d'autres étapes. Soyez explicite sur ce que vous téléchargez afin de ne pas publier accidentellement vos identifiants sur Internet. Listez des répertoires ou des types de fichiers spécifiques plutôt que d'englober l'intégralité de l'espace de travail, et ajoutez .env, *.pem, et des fichiers similaires à vos .gitignore modèles d'exclusion et d'artefacts.
Références d'actions mutables
7. Épingler toutes les actions tierces à un SHA de commit complet, et non à un tag ou une branche
Les tags sont utiles comme raccourci, mais l'inconvénient est qu'ils ne sont pas statiques. Les tags et les branches sont mutables, ce qui signifie qu'un propriétaire de dépôt peut les rediriger vers un commit différent à tout moment, et c'est attendu avec des tags comme @main. Lors de la référence à une version spécifique avec uses: some-action@v3, nous nous attendons à ce que le commit reste le même. Cependant, si le compte du mainteneur de l'action est compromis, un attaquant peut rediriger cette balise vers un commit malveillant et chaque workflow en aval le récupérera lors de la prochaine exécution sans PR ni autre indication. Pour éviter cela, la meilleure pratique consiste à épingler le SHA complet du commit à la place : uses: some-action@abc123def456..... Cela peut bien sûr être un peu fastidieux à gérer, vous pouvez donc utiliser Dependabot, Renovate ou pinact pour maintenir les SHA épinglés à jour. Les fichiers de verrouillage natifs sont sur la feuille de route de GHA, donc nous espérons qu'ils seront disponibles d'ici 2027.
Dans la nature : En mars 2025, des attaquants ont redirigé 76 des 77 balises de version dans aquasecurity/trivy-action vers des commits malveillants contenant un infostealer. Chaque workflow faisant référence à ces balises par leur nom a automatiquement récupéré le code malveillant.
8. Valider les actions tierces avant leur adoption
Avant d'ajouter une uses : , passez deux minutes sur le dépôt de l'action. Vérifiez si le créateur est vérifié par GitHub, quand le dépôt a été maintenu pour la dernière fois, combien de contributeurs il a, et quel est son score OpenSSF Scorecard. Une action largement utilisée provenant d'un seul mainteneur non vérifié et sans activité récente est une cible de grande valeur pour une prise de contrôle de compte.
9. Préférer les actions avec moins de dépendances transitives
Plus de dépendances vous rendent plus vulnérable aux attaques de la chaîne d’approvisionnement, alors choisissez des actions avec moins de dépendances lorsque des options sont disponibles. Une action peut elle-même référencer d'autres actions, et ces dépendances transitives sont résolues à l'exécution. Épingler le SHA de votre dépendance directe ne vous protège pas si elle tire ses propres dépendances de manière mutable.
Dans la nature : Le compromis tj-actions s'est propagé en partie par ce mécanisme. Les workflows en aval épinglés à tj-actions/changed-files, mais changed-files référençait transitivement reviewdog/action-setup par une balise mutable, de sorte que lorsque reviewdog a été compromis, chaque pipeline en aval a exécuté le code malveillant.
Dépendances de paquets mutables
10. Épingler explicitement les versions des paquets npm et PyPI
Les plages de versions flottantes comme ^1.2.0 ou >=2.0.0 signifient que votre workflow installe la dernière version correspondante à l'exécution. Si un paquet est compromis et qu'une nouvelle version malveillante est publiée dans votre plage, votre workflow l'intègre automatiquement lors de la prochaine exécution. Épinglez à des versions exactes (1.2.3) afin que votre workflow n'installe que ce que vous avez explicitement choisi. Ne flottez pas sur la plage.
En situation réelle : L'attaque Ultralytics a démontré comment une version de package compromise publiée sur PyPI peut atteindre des workflows qui ne sont pas épinglés. La première version malveillante est restée active pendant des heures avant sa détection, suffisamment longtemps pour affecter les builds qui intègrent des dépendances flottantes.
11. Définir un âge de publication minimum là où votre gestionnaire de paquets le supporte (pnpm, yarn)
Même avec des versions épinglées, un package malveillant nouvellement publié peut correspondre à une version exacte vers laquelle vous choisissez de mettre à niveau ultérieurement. Les paramètres d'âge minimum de publication indiquent à votre gestionnaire de packages de refuser les packages publiés il y a moins d'un certain temps, généralement 72 heures, donnant ainsi à la communauté le temps de détecter et de signaler les versions malveillantes avant qu'elles n'atteignent vos builds. pnpm et yarn prennent en charge cela nativement, mais npm ne le fait pas encore. Aikido Safe Chain peut couvrir ce cas pour npm (voir ci-dessous).
12. Vérifier la provenance des paquets en utilisant des attestations lorsque disponibles
Certains registres de packages prennent désormais en charge les attestations de provenance, qui sont des enregistrements cryptographiques liant un package publié au commit source spécifique et au pipeline de build qui l'a produit. La vérification des attestations avant d'installer un package confirme qu'il a été construit à partir de la source qu'il prétend. npm prend en charge cela pour les packages publiés via GitHub Actions. Il s'agit encore d'une pratique récente et le support des outils n'est pas encore complet, mais il est utile de l'activer là où votre registre et votre gestionnaire de packages le prennent en charge.
Gestion des secrets
13. Référencer les secrets via des variables d'environnement, jamais via des arguments de ligne de commande
Les arguments de ligne de commande sont visibles dans les listes de processus, de sorte que d'autres processus sur le runner ayant accès /proc peuvent les lire. Passer un secret en tant que variable d'environnement le maintient hors de la table des processus. Dans votre workflow, définissez le secret dans un env: bloc et référencez-le $SECRET_NAME dans la commande shell plutôt que ${{ secrets.MY_SECRET }} en ligne.
En situation réelle : tj-actions a exfiltré des secrets en les imprimant dans le journal (le problème d'écho/masquage), et l'attaque Trivy a entraîné le vol d'un PAT. La gestion des secrets consiste à limiter l'accès aux secrets dans tout le système afin que, si quelque chose tourne mal, un attaquant ne puisse pas repartir avec des identifiants fonctionnels.
14. Définir la portée des secrets au niveau du dépôt pour des environnements GitHub spécifiques lorsque possible
Les environnements GitHub vous permettent de protéger les secrets derrière des règles de protection de déploiement, de sorte qu'un secret comme PROD_DB_PASSWORD n'est accessible qu'aux workflows ciblant l'environnement de production. Sans cela, tout workflow du dépôt peut lire n'importe quel secret au niveau du dépôt. Vous pouvez configurer cela sous Paramètres > Environnements.
15. Définir la portée des secrets dans le workflow au niveau de l'étape, et non au niveau du job
La délimitation des secrets aux étapes individuelles d'un job qui en a besoin applique le principe du moindre privilège pour limiter le rayon d'impact si une action est compromise. Un secret déclaré dans un bloc de niveau de job env: est lisible par chaque étape de ce job, y compris les actions tierces.
En situation réelle : Dans l'attaque Shai-Hulud, des jetons avec une portée de workflow ont été réutilisés chez plusieurs victimes. Une portée plus étroite aurait contenu le rayon d'impact.
16. Utiliser OIDC pour obtenir des identifiants cloud de courte durée au lieu de secrets statiques de longue durée lorsque le fournisseur cloud le supporte (AWS, Azure, GCP)
Les identifiants cloud statiques stockés en tant que secrets GitHub sont valides indéfiniment. S'ils sont volés, la fenêtre de vulnérabilité est grande ouverte. OIDC permet à votre workflow de demander directement un jeton de courte durée à AWS, Azure ou GCP, limité à la tâche et valide pour quelques minutes, ainsi il n'y a pas d'identifiants à voler pour un attaquant. AWS, Azure et GCP prennent tous en charge cette fonctionnalité nativement. La configuration implique d'établir une relation de confiance entre votre fournisseur cloud et le point de terminaison OIDC de GitHub.
17. Limitez les règles de publication de confiance npm à un fichier de workflow spécifique et à une branche protégée, et non à l'ensemble du dépôt
Dans les paramètres de publication de confiance de npm, spécifiez le nom exact du fichier de workflow (par exemple, release.yml) et la branche (par exemple, principal) plutôt que de faire confiance à l'ensemble du dépôt. Associez cela à des règles de protection de branche qui exigent des PR et bloquent les poussées forcées, afin qu'aucun workflow en dehors de ce chemin ne puisse demander un jeton de publication.
Cas concret : Lors de l'attaque Mini Shai-Hulud TanStack de mai 2026, un attaquant a poussé un commit orphelin sans historique parent vers le dépôt TanStack/router. Étant donné que la règle de publication de confiance de npm faisait confiance à l'ensemble du dépôt plutôt qu'à un workflow spécifique sur une branche protégée, le commit a déclenché un workflow qui a réussi à générer un jeton de publication. L'attaquant l'a utilisé pour publier des versions malveillantes de 84 packages à travers l'écosystème TanStack en l'espace de six minutes.
18. Exiger une approbation humaine pour les exécutions de workflow qui utilisent des environnements de production
Configurez votre environnement GitHub pour exiger qu'une personne examine un workflow avant qu'il ne puisse accéder aux secrets d'un environnement ou y déployer. C'est plus réaliste pour les petites équipes ou celles qui déploient rarement. Pour les équipes plus importantes, la version plus évolutive est OIDC avec des identifiants de courte durée.
19. Éviter d'afficher ou d'écho les valeurs secrètes, même dans les sorties de débogage
GitHub masque les valeurs de secrets connues dans les journaux, mais uniquement pour les correspondances exactes. Ainsi, si un secret est encodé en base64 ou divisé sur deux appels echo, le masquage ne fonctionne pas. La règle la plus sûre est de ne jamais faire écho aux secrets. Si vous devez vérifier qu'un secret est défini, vérifiez la présence d'une chaîne non vide plutôt que d'afficher la valeur.
Runners
20. Ne pas utiliser de runners auto-hébergés sur des dépôts publics
N'importe qui peut ouvrir une PR sur un dépôt public, ce qui signifie que n'importe qui peut potentiellement déclencher l'exécution d'un workflow. Les paramètres d'approbation par défaut de GitHub pour les contributeurs débutants atténuent ce risque sur les runners hébergés par GitHub, où l'environnement est de toute façon éphémère et isolé. Sur un runner auto-hébergé, un paramètre d'approbation mal configuré ou trop permissif signifie que la même PR déclenche l'exécution de code sur votre infrastructure. GitHub recommande d'utiliser des runners hébergés par GitHub pour les dépôts publics, et de renforcer la politique d'approbation pour « exiger l'approbation pour tous les collaborateurs externes ».
Dans la pratique : Des chercheurs ont exécuté une attaque par chaîne d'approvisionnement sur PyTorch en soumettant une PR triviale qui a déclenché un workflow sur un runner auto-hébergé, obtenant un accès root à la machine.
21. Utiliser des runners éphémères plutôt que des runners statiques persistants
Un runner statique conserve son état entre les tâches, ce qui signifie qu'une tâche compromise peut laisser des fichiers malveillants, des binaires modifiés ou des caches empoisonnés qui affectent les exécutions suivantes sur cette machine. Les runners éphémères démarrent de manière propre et sont détruits après chaque tâche. Utilisez Actions Runner Controller pour les configurations basées sur Kubernetes, ou passez l'option --ephemeral lors de l'enregistrement manuel d'un runner.
Dans la pratique : Shai-Hulud a enregistré des runners auto-hébergés persistants sur des dépôts compromis et les a utilisés comme canal C2 persistant, invisible pour la surveillance réseau car tout le trafic passait par github.com.
22. Restreindre l'accès réseau sortant des runners à une liste blanche
L'action la plus précieuse d'un workflow compromis est souvent l'exfiltration de secrets vers un serveur contrôlé par un attaquant. Restreindre l'accès réseau sortant aux seuls domaines dont votre workflow a réellement besoin rend cela beaucoup plus difficile. Harden-Runner et bullfrog fonctionnent tous deux sur les runners hébergés par GitHub. Pour les runners auto-hébergés, les règles de pare-feu au niveau du réseau accomplissent la même chose.
Si vous utilisez GitHub Enterprise avec des runners auto-hébergés, les listes blanches d'IP pour les jetons ajoutent un contrôle dans l'autre sens. Cela restreint les IP qui peuvent utiliser un jeton, ainsi un jeton volé ne peut pas être utilisé depuis l'infrastructure d'un attaquant.
Permissions des jetons
23. Définir par défaut GITHUB_TOKEN permissions en lecture seule au niveau de l'organisation ou du dépôt.
Le GITHUB_TOKEN Le GITHUB_TOKEN est automatiquement disponible dans chaque exécution de workflow. Par défaut, il dispose d'un accès étendu en lecture et écriture sur l'ensemble du dépôt, donc tout workflow compromis peut écrire dans votre dépôt, créer des releases ou approuver des PR. Définissez la valeur par défaut en lecture seule sous Paramètres > Actions > Général, puis accordez des permissions d'écriture explicitement uniquement là où c'est nécessaire.
24. Déclarer explicitement permissions : au niveau du workflow ou de la tâche pour restreindre les tâches individuelles.
Définir une valeur par défaut en lecture seule au niveau de l'organisation crée la valeur par défaut, mais certaines tâches ont bien sûr besoin de plus de permissions. Lorsqu'une tâche nécessite un accès élevé, déclarez un bloc permissions : au niveau de la tâche plutôt qu'au niveau du workflow. De cette façon, le jeton avec permissions élevées est limité à cette seule tâche, et toutes les autres tâches du workflow restent en lecture seule.
jobs:
deploy:
permissions:
contents: read
id-token: writeDans la pratique : Les attaques tj-actions, Trivy et prt-scan ont toutes tiré parti de jetons disposant de plus d'accès que nécessaire. Le renforcement des permissions des jetons réduit la surface d'attaque globale.
Paramètres de l'organisation et des dépôts
25. Désactiver la capacité des Actions à approuver les PR
Par défaut, GitHub permet aux workflows d'approuver les pull requests en utilisant le GITHUB_TOKEN. Cela signifie qu'un workflow compromis pourrait approuver ses propres modifications malveillantes sans passer par un processus de révision. Désactivez cette option sous Paramètres > Actions > Général > « Autoriser les actions GitHub à créer et approuver les pull requests. » (Certaines équipes le laissent activé pour l'auto-merge de Dependabot, mais il est préférable de donner à Dependabot un compte bot dédié avec des permissions de réviseur explicites plutôt que d'activer l'approbation de workflow pour toute l'organisation.)
26. Restreindre les sources d'Action aux créateurs vérifiés ou à des dépôts spécifiques
Le paramètre par défaut dans GitHub permet d'utiliser n'importe quelle action publiée sur le GitHub Marketplace dans vos workflows. Restreindre les sources aux créateurs vérifiés ou à une liste blanche spécifique signifie qu'une action malveillante nouvellement publiée ne peut pas être adoptée dans vos workflows sans approbation explicite. Au lieu de cela, les développeurs devraient demander l'approbation à la personne responsable des paramètres de votre organisation GitHub (généralement l'équipe plateforme ou DevOps), qui pourra l'ajouter à la liste blanche. Configurez cela sous Paramètres > Actions > Général > « Autoriser les actions et les workflows réutilisables. »
Dans la pratique : La compromission de tj-actions a affecté 23 000 dépôts en partie parce que les équipes n'avaient aucune restriction sur les actions qu'elles pouvaient intégrer.
27. Surveiller les enregistrements inattendus de runners auto-hébergés et les nouveaux dépôts publics créés dans votre organisation
Un jeton volé permet à un attaquant d'enregistrer un runner malveillant pour implanter une porte dérobée persistante. Les attaquants créent également régulièrement des dépôts publics pour y déverser des identifiants volés. L'enregistrement de runners et la création de dépôts sont des événements que votre équipe doit voir immédiatement, mais GitHub n'alerte pas sur ceux-ci par défaut. La solution de contournement est de les surveiller manuellement. Transférez le journal d'audit de votre organisation vers un SIEM et alertez sur self_hosted_runners.register événements et la création de dépôts. Sans SIEM, vous pouvez interroger le journal d'audit via l'API GitHub ou vérifier manuellement sous Paramètres > Journal d'audit.
Dans la pratique : Lors de l'attaque Shai-Hulud, des machines compromises ont été enregistrées comme runners auto-hébergés nommés SHA1HULUD, et des identifiants volés ont été utilisés pour créer des dépôts publics servant de points d'exfiltration. La surveillance de ces deux éléments aurait permis de détecter la campagne tôt.
28. Utiliser CODEOWNERS pour exiger une révision axée sur la sécurité pour les modifications apportées à .github/workflows/
Les fichiers de workflow sont du code qui s'exécute avec accès à vos secrets et jetons. Sans règles de propriété explicites, tout développeur ayant un accès en écriture au dépôt peut modifier un workflow et le merge sans révision de sécurité. Ajoutez une entrée CODEOWNERS pointant .github/workflows/ vers un réviseur ou une équipe qui comprend les considérations de sécurité, et combinez-la avec des règles de protection de branche qui exigent l'approbation de CODEOWNERS avant le merge.
Protéger les workflows GitHub Actions avec Aikido
Aikido détecte les workflows GitHub Actions non sécurisés et aide les développeurs à les corriger avant qu'ils ne soient exploités.
Il détecte des problèmes tels que :
- Utilisation non sécurisée de
pull_request_target - Injection de script via des éléments non fiables
github.*d'entrée - Injection de templates et de prompts dans les workflows basés sur l'IA
- Actions tierces non épinglées
- Surprivilégiées
GITHUB_TOKENPermissions - Gestion risquée des secrets dans les workflows
Aikido peut également :
- Créer automatiquement des PRs de correction
- Détecter les modèles de workflow non sécurisés à travers les dépôts
- Signaler les références mutables d'Actions GitHub qui devraient être épinglées à un SHA de commit complet
Aikido Safe Chain aide à protéger les installations de packages en :
- Bloquer les packages npm et PyPI malveillants connus
- Appliquer des politiques d'âge minimum pour les packages
Par exemple, Aikido détecte les entrées utilisateur non assainies dans run : les étapes et suggère automatiquement le modèle de variable d'environnement plus sûr.
Vous êtes prêt à sécuriser les workflows GitHub Actions
Gardez cette checklist à portée de main lorsque vous travaillez à la sécurisation des workflows GitHub Actions. Essayez de mettre en place rapidement les éléments que vous pouvez réaliser en quelques minutes, et partagez-les avec vos coéquipiers afin que tout le monde soit au courant des changements.
FAQ : Sécurité des GitHub Actions
Quel est le moyen le plus courant d'exploiter les workflows GitHub Actions ?
L'injection de scripts est le vecteur le plus fréquent. Les attaquants insèrent du code malveillant dans les noms de branches, les titres de PR ou les corps d'issues, et les workflows qui interpolent ces valeurs directement dans run : les étapes les exécutent comme des commandes shell. Épingler les actions à des SHAs de commit et utiliser des variables d'environnement au lieu de l'interpolation en ligne réduit la majeure partie de cette surface d'attaque.
Qu'est-ce que pull_request_target et pourquoi est-ce dangereux ?
pull_request_target est un déclencheur de workflow qui s'exécute avec un accès aux secrets du dépôt de base, même lorsque la PR déclenchante provient d'un fork. Étant donné que n'importe qui peut ouvrir une PR contre un dépôt public, tout workflow utilisant ce déclencheur expose ses secrets à des contributeurs externes. Évitez-le entièrement sur les dépôts publics.
Que signifie « épingler les actions à un SHA de commit » et pourquoi est-ce important ?
La plupart des workflows référencent des actions tierces par tag, comme uses: some-action@v3. Les tags sont mutables, donc si le compte d'un mainteneur d'action est compromis, un attaquant peut rediriger silencieusement ce tag vers du code malveillant. Épingler à un SHA de commit complet comme uses: some-action@abc123... signifie que votre workflow n'exécute que ce que vous avez examiné.
Dois-je utiliser des runners auto-hébergés sur des dépôts publics ?
Non. N'importe qui peut ouvrir une PR contre un dépôt public et déclencher un workflow. Sur les runners hébergés par GitHub, c'est gérable car l'environnement est éphémère et isolé. Sur un runner auto-hébergé, une politique d'approbation mal configurée signifie que des contributeurs externes peuvent exécuter du code directement sur votre infrastructure.
Qu'est-ce que l'OIDC et pourquoi est-ce mieux que de stocker les identifiants cloud comme secrets ?
Les identifiants statiques stockés comme secrets GitHub sont valides indéfiniment, donc un secret volé reste dangereux longtemps après la violation. L'OIDC permet à votre workflow de demander un jeton de courte durée à AWS, Azure ou GCP, limité à ce travail spécifique, et valide pour quelques minutes. Il n'y a pas d'identifiants de longue durée à voler.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "TechArticle",
"@id": "https://www.aikido.dev/blog/checklist-github-actions#article",
"headline": "The complete GitHub Actions security checklist",
"description": "GitHub Actions misconfigurations have been behind some of the biggest supply chain attacks of 2025 and 2026. This checklist covers the real attack vectors, what went wrong, and how to fix it.",
"url": "https://www.aikido.dev/blog/checklist-github-actions",
"datePublished": "2026-05-11",
"dateModified": "2026-05-11",
"inLanguage": "en-US",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://www.aikido.dev/blog/checklist-github-actions"
},
"author": {
"@type": "Person",
"@id": "https://www.aikido.dev/team-members/dania-durnas",
"name": "Dania Durnas",
"jobTitle": "Content Marketing",
"url": "https://www.aikido.dev/team-members/dania-durnas",
"worksFor": {
"@type": "Organization",
"name": "Aikido Security",
"url": "https://www.aikido.dev"
},
"sameAs": [
"https://www.linkedin.com/in/daniadurnas/"
]
},
"publisher": {
"@type": "Organization",
"name": "Aikido Security",
"url": "https://www.aikido.dev",
"logo": {
"@type": "ImageObject",
"url": "https://www.aikido.dev/logo.png"
}
},
"keywords": [
"GitHub Actions security",
"CI/CD security",
"supply chain attack",
"pull_request_target",
"script injection",
"mutable action references",
"tj-actions",
"workflow security",
"GitHub Actions checklist",
"DevSecOps",
"secrets management",
"self-hosted runners",
"OIDC",
"GITHUB_TOKEN",
"prt-scan",
"Trivy attack",
"Ultralytics attack",
"Safe Chain"
],
"about": [
{
"@type": "Thing",
"name": "GitHub Actions",
"url": "https://github.com/features/actions"
},
{
"@type": "Thing",
"name": "Supply Chain Security"
},
{
"@type": "Thing",
"name": "CI/CD Pipeline Security"
}
],
"mentions": [
{"@type": "SoftwareApplication", "name": "GitHub Actions"},
{"@type": "SoftwareApplication", "name": "Safe Chain", "url": "https://aikido.dev/safe-chain"},
{"@type": "SoftwareApplication", "name": "Aikido Security", "url": "https://www.aikido.dev"},
{"@type": "Thing", "name": "tj-actions supply chain attack"},
{"@type": "Thing", "name": "Trivy attack"},
{"@type": "Thing", "name": "Ultralytics attack"},
{"@type": "Thing", "name": "prt-scan campaign"},
{"@type": "Thing", "name": "CVE-2025-30066"}
],
"speakable": {
"@type": "SpeakableSpecification",
"cssSelector": ["h1", "h2", ".intro"]
},
"articleSection": [
"Trigger configuration",
"Handling untrusted input",
"Artifact handling",
"Mutable action references",
"Mutable package dependencies",
"Secrets handling",
"Runners",
"Token permissions",
"Org and repo settings"
],
"timeRequired": "PT15M"
},
{
"@type": "BreadcrumbList",
"@id": "https://www.aikido.dev/blog/checklist-github-actions#breadcrumb",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://www.aikido.dev"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": "https://www.aikido.dev/blog"
},
{
"@type": "ListItem",
"position": 3,
"name": "The complete GitHub Actions security checklist",
"item": "https://www.aikido.dev/blog/checklist-github-actions"
}
]
},
{
"@type": "Organization",
"@id": "https://www.aikido.dev#organization",
"name": "Aikido Security",
"url": "https://www.aikido.dev",
"logo": {
"@type": "ImageObject",
"url": "https://www.aikido.dev/logo.png"
}
}
]
}
</script>

