Depuis deux ans, je répète la même phrase aux équipes que j'accompagne : « passez à OIDC, supprimez vos AWS_ACCESS_KEY_ID des secrets GitHub ». C'est resté vrai. Mais après les attaques de mai 2026 — actions-cool/issues-helper, puis Megalodon et ses 5 561 dépôts piégés — je ne formule plus la consigne de la même façon.
Le problème n'est pas OIDC. Le problème, c'est qu'OIDC est devenu un point final dans le discours sécurité, alors que ce devrait être un point de départ. Les deux attaques de mai n'utilisent pas exactement la même technique, mais elles exploitent la même frontière oubliée : le runner CI/CD.
Avec actions-cool/issues-helper, l'attaquant détourne des tags d'action et fait exécuter un imposter commit capable de lire la mémoire du process Runner.Worker. Avec Megalodon, il injecte directement des workflows GitHub Actions malveillants dans des dépôts publics et collecte variables d'environnement, fichiers de credentials, kubeconfigs, clés SSH et tokens OIDC.
Dans les deux cas, la leçon est la même : OIDC supprime les credentials cloud statiques, mais il ne rend pas inoffensif le code qui s'exécute dans un job autorisé à manipuler des credentials temporaires. Ce billet est un correctif de mon propre discours — je n'arrête pas de recommander OIDC, je précise ce qu'il faut empiler autour pour que l'argument tienne en 2026.
Ce qu'OIDC résout vraiment
Le scénario historique tient en une ligne : un secret AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY stocké dans GitHub Secrets, valable des mois, copié à la main d'un collaborateur à l'autre, oublié dans un fork, exfiltré le jour où un workflow tierce-partie tourne avec trop de droits.
OIDC casse ce schéma. GitHub signe un JWT qui décrit l'identité du job (repo, ref, workflow, job, environment). Le cloud cible vérifie cette signature, applique sa trust policy, et renvoie des credentials temporaires valables 15 minutes à 1 heure. Plus aucun credential long à protéger côté GitHub.
C'est exactement ce que je documente dans le guide OIDC GitHub Actions : authentification sans secrets et son pendant cloud OIDC AWS, Azure, GCP. La mise en œuvre est simple, l'amélioration de posture est réelle, et je ne reviens pas en arrière sur cette recommandation.
Ce qu'OIDC ne résout pas
Le credential temporaire obtenu via OIDC n'est pas un secret stocké durablement dans GitHub, mais il devient bien une donnée exploitable pendant l'exécution du job : variable d'environnement (AWS_ACCESS_KEY_ID, AWS_SESSION_TOKEN exportés par défaut par aws-actions/configure-aws-credentials), fichier de credentials, token demandé par une étape, ou artefact accessible depuis le contexte du runner selon l'action utilisée.
Tout code qui s'exécute pendant ce temps dans le même runner voit ce credential. Une action tierce malveillante, un workflow injecté, un script post-install d'une dépendance npm, une lecture de /proc/<PID>/environ ou /proc/<PID>/mem — tous peuvent extraire ces données sensibles.
C'est exactement le mode opératoire de Megalodon : injection directe d'un workflow malveillant qui collecte les variables d'environnement du runner, les fichiers de credentials, les kubeconfigs, et les tokens OIDC demandés depuis le job. Un token cloud valable 15 minutes suffit à un attaquant industriel — il a les outils pour l'exfiltrer et le réutiliser dans la fenêtre de validité.
L'illusion à corriger est donc simple : « OIDC = pas de credentials volables » est faux. OIDC supprime les credentials persistants. Il ne supprime pas les credentials éphémères. Les deux familles sont volables, mais avec des fenêtres de risque très différentes.
Le scénario que je n'avais pas vu venir
Imaginez un workflow deploy.yml propre, comme j'en ai écrit des dizaines. Permissions par défaut en lecture seule. id-token: write posé uniquement sur le job de déploiement. Trust policy AWS qui exige repo:my-org/my-repo:ref:refs/heads/main. Action aws-actions/configure-aws-credentials épinglée par SHA. Sur le papier, c'est ce qu'on appelle un workflow durci.
jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: actions/checkout@<SHA> - uses: aws-actions/configure-aws-credentials@<SHA> with: role-to-assume: arn:aws:iam::123456789012:role/deploy aws-region: eu-west-3 - run: ./scripts/deploy.shLe problème ne se voit pas tant qu'aucune étape étrangère n'est insérée. Le jour où un attaquant injecte une étape avant ou après configure-aws-credentials — via un commit malveillant sur .github/workflows/, une action tierce compromise par tag, ou une dépendance qui s'exécute pendant ./scripts/deploy.sh — il récupère soit le JWT GitHub (avant l'échange), soit les credentials AWS temporaires (après l'échange).
Dans les deux cas, l'attaquant a une fenêtre de 15 à 60 minutes pour assumer le rôle deploy depuis sa propre infrastructure. C'est largement assez pour automatiser une exfiltration ou planter une backdoor cloud.
Les quatre couches à empiler autour d'OIDC
OIDC est nécessaire mais pas suffisant. Voici les quatre couches que je considère désormais comme obligatoires dans toute architecture GitHub Actions qui touche à la production.
Une trust policy cloud stricte. La condition sub du JWT doit être contrainte au maximum : repo précis, branche précise ou environment précis. Évitez les StringLike larges et les wildcards d'organisation — préférez StringEquals sur un sub précis quand c'est possible. C'est la première barrière, et c'est elle qui empêche un workflow forké ou une branche de test d'assumer le rôle de production.
Des permissions GitHub minimales. id-token: write posé uniquement sur le job qui en a besoin, jamais au niveau du workflow. contents: read par défaut. Voir le guide Permissions GitHub Actions GITHUB_TOKEN pour le détail.
Un contrôle d'egress sur le runner. C'est la couche la plus oubliée. Si un payload malveillant ne peut pas joindre l'infrastructure de l'attaquant, l'exfiltration des credentials échoue. Harden-Runner en mode block ou une allow-list firewall sur un runner self-hosted font exactement ce travail. Je détaille les patterns dans Hardening de l'environnement de build.
Une gouvernance forte des workflows. Le fichier .github/workflows/ doit être traité comme du code de production : CODEOWNERS qui force une revue par l'équipe sécurité, épinglage SHA des actions tierces, protection de branche stricte. C'est précisément la leçon de l'attaque sur actions-cool/issues-helper qui a précédé Megalodon de quelques heures.
Aucune de ces couches ne remplace les autres. Et aucune ne dispense d'OIDC. Elles s'empilent.
Durcir une trust policy AWS post-Megalodon
C'est l'erreur que je vois le plus souvent en audit : une trust policy AWS écrite pour « marcher partout dans l'organisation », donc trop large pour résister à une attaque ciblée.
Le pattern à fuir :
{ "Condition": { "StringLike": { "token.actions.githubusercontent.com:sub": "repo:my-org/*" } }}Le StringLike avec wildcard autorise n'importe quel repo de l'organisation à assumer le rôle. N'importe quelle branche, n'importe quel workflow, n'importe quel environment. Le jour où un repo annexe de l'organisation est compromis, l'attaquant assume le rôle de production sans rien forcer.
Le pattern à viser :
{ "Condition": { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com", "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:environment:production" } }}StringEquals au lieu de StringLike. Sub limité à un repo précis et à un environment précis — pas une branche, parce qu'un attaquant qui pousse un commit sur main reste légitime, alors qu'un attaquant qui demande l'assumption depuis l'environment production doit d'abord passer le gate d'approbation de cet environment. C'est l'enchaînement qui durcit, pas chaque maillon isolément.
StringLike n'est pas interdit en soi — la doc GitHub elle-même montre des exemples avec wildcard pour autoriser plusieurs branches ou environments d'un même repo. Le problème, c'est le wildcard trop large (repo:my-org/*). Si StringLike est nécessaire, limitez-le à un motif strict, documenté et testé.
Le format exact du claim sub dépend du déclencheur (branche, tag, environment, pull request). Vérifiez la doc avant de coller un exemple dans votre trust policy — un sub mal formé bloque le déploiement sans message clair.
Le piège des environnements GitHub
Les GitHub Environments restent sous-utilisés. C'est dommage, parce qu'ils ajoutent exactement la pièce qui manque dans un workflow OIDC : un gate humain avant l'échange du token.
Un environment production peut exiger :
- des required reviewers (approbation humaine obligatoire avant que le job ne démarre) ;
- un wait timer (délai forcé avant exécution) ;
- des branches autorisées (seul
mainpeut déployer enproduction) ; - des secrets scopés à l'environment, invisibles aux autres jobs.
Couplé à une trust policy AWS qui exige environment:production dans le sub claim, ça donne une chaîne défensive cohérente : pas de déploiement sans approbation humaine, et pas de token OIDC valable sans environment validé. Un environment protégé n'aurait pas rendu Megalodon impossible, mais il aurait changé la dynamique — le workflow injecté n'aurait plus pu obtenir silencieusement un token de production sans passer par une règle d'approbation, une restriction de branche ou un contrôle de déploiement visible.
C'est la couche que j'oubliais le plus dans mes recommandations. Je ne l'oublie plus.
Ce que j'ai changé dans mon discours
Avant mai 2026, je disais : « passez à OIDC ». Point final, comme si l'absence de credential statique réglait le sujet.
Aujourd'hui, je dis : « passez à OIDC, ET imposez un environment protégé, ET scopez la trust policy au sub précis, ET coupez l'egress du runner, ET protégez .github/workflows/ par CODEOWNERS ». Cinq exigences au lieu d'une.
C'est plus lourd à formuler. C'est aussi plus honnête. OIDC reste la bonne fondation. Mais une fondation seule n'est pas une maison, et une attaque comme Megalodon vient rappeler qu'un attaquant industriel ne s'arrête pas au mur le plus visible — il cherche la fenêtre ouverte.
Erreurs fréquentes que je vois en audit
Quatre patterns reviennent dans presque chaque audit GitHub Actions :
id-token: writeau niveau du workflow au lieu du job. Tous les jobs du workflow peuvent alors demander un token OIDC, même ceux qui n'en ont pas besoin. C'est la première chose à corriger.- Trust policy en
StringLikerepo:my-org/*. À remplacer parStringEqualssur un sub précis, idéalement avec un environment. - Pas d'environment protégé sur le job de déploiement. Sans gate humain, n'importe quel commit qui passe la CI déploie en production avec les credentials cloud.
- Aucun egress control sur les runners, ni
Harden-Runner, ni allow-list firewall. Un payload qui vole les credentials peut les exfiltrer sans aucune contrainte réseau.
Aucune de ces erreurs n'est dramatique isolément. Les quatre ensemble forment exactement le terrain de jeu qu'attendent les attaques de la famille de Megalodon.
Ce que ce billet ne dit pas
Ce billet ne dit pas qu'OIDC est une mauvaise pratique.
Au contraire : OIDC reste préférable aux credentials cloud statiques stockés dans GitHub Secrets. Il réduit fortement la durée de vie des credentials et permet de contraindre l'identité du workflow via des claims comme repo, ref, environment ou workflow.
Mais OIDC ne protège pas contre un workflow malveillant déjà autorisé à demander un token. La bonne question n'est donc pas « OIDC ou pas OIDC ? ». La bonne question est : « quel code peut s'exécuter dans le job qui a id-token: write ? »
Checklist rapide post-Megalodon
Le tour d'audit que je conseille de passer sur chaque dépôt dans les jours qui suivent un incident comme Megalodon. Court, opérationnel, et largement à la portée d'une demi-journée par projet.
- Vérifier tous les workflows qui déclarent
id-token: write. - Déplacer
id-token: writeau niveau du job, jamais au niveau global. - Associer les jobs de production à un GitHub Environment protégé.
- Restreindre la trust policy cloud à un
subprécis. - Ajouter
CODEOWNERSsur.github/workflows/. - Bloquer ou surveiller l'egress des runners.
- Rechercher les workflows nommés
SysDiagetOptimize-Build(IOCs Megalodon). - Auditer les commits récents sur
.github/workflows/depuis le 18 mai 2026.
À retenir
- OIDC supprime les credentials cloud persistants, pas les credentials éphémères qui vivent dans le runner pendant le job.
- Megalodon et actions-cool exploitent cette zone grise : voler le token éphémère depuis la mémoire du runner suffit à l'attaquant.
- OIDC seul n'est plus une réponse complète en 2026. Il faut l'empiler avec trust policy stricte, permissions minimales, egress control et gouvernance des workflows.
- La trust policy doit être en
StringEqualssur un sub précis incluant l'environment, pas enStringLikesur un wildcard d'organisation. - Les GitHub Environments avec required reviewers sont la pièce la plus oubliée et la plus efficace pour bloquer un workflow injecté.
id-token: writeau niveau du job uniquement, jamais au niveau du workflow.- Le fichier
.github/workflows/est du code de production : CODEOWNERS, revue obligatoire, épinglage SHA des actions tierces.