Aller au contenu
CI/CD & Automatisation medium

Attaques supply chain sur GitHub Actions : cas réels et protections

17 min de lecture

Ce guide vous permet de comprendre et de bloquer les attaques supply chain qui ont compromis des dizaines de milliers de dépôts en 2025-2026 via GitHub Actions. Vous apprendrez à reconnaître les vecteurs d’exploitation, à auditer vos workflows existants, et à déployer une défense en profondeur. Prérequis : connaître les bases de GitHub Actions et avoir lu le guide Sécurité GitHub Actions.

Un workflow GitHub Actions mal configuré donne à un attaquant un accès privilégié à toute votre chaîne de livraison. Ce n’est pas théorique : en 2025, des attaques réelles ont exploité ces failles pour voler des secrets, injecter du code malveillant et compromettre des packages distribués à des milliers d’utilisateurs.

Voici ce qu’un attaquant peut obtenir en compromettant un seul workflow :

RessourceImpactCas réel
Secrets (tokens, credentials)Vol et réutilisation pour accéder à vos systèmes cloud, registres, APIsGhostAction : 3 325 secrets volés (AWS, PyPI, npm)
Code sourceInjection de backdoors invisibles dans le codeShai Hulud v2 : ver qui se réplique dans le code
Artefacts (images, binaires)Distribution de malware à vos utilisateursNx s1ngularity : packages npm infectés
Registres (npm, Docker Hub)Publication de versions compromises sous votre nom1 700 versions npm infectées par Shai Hulud v2
ProductionDéploiement de code malveillantAccès cloud via tokens volés

L’action la plus populaire pour détecter les fichiers modifiés (23 000+ dépôts). Un attaquant a volé un PAT (Personal Access Token) du mainteneur, puis a réécrit les tags existants pour pointer vers du code malveillant qui exfiltrait tous les secrets dans les logs publics du workflow.

Vecteurs exploités :

  • Tags Git mutables (les utilisateurs faisaient confiance à @v1)
  • Pas d’épinglage par SHA
  • Secrets visibles dans les logs de workflow

Leçon : un tag @v1 n’est qu’une étiquette mobile. Seul un SHA garantit que le code exécuté est celui que vous avez validé.

327 comptes compromis, 817 repos infiltrés, 3 325 secrets volés. Les attaquants ont exploité des workflows utilisant pull_request_target avec des permissions trop larges pour obtenir un accès en écriture. Les secrets volés (clés AWS, tokens PyPI et npm) étaient envoyés vers des serveurs contrôlés par les attaquants.

Vecteurs exploités :

  • pull_request_target avec checkout du code de la PR
  • GITHUB_TOKEN avec permissions write par défaut
  • Pas de contrôle des connexions réseau sortantes

Le premier ver auto-réplicant à grande échelle sur GitHub Actions. 20 000+ dépôts infectés, 1 700 versions npm compromises. Le ver exploitait pull_request_target pour obtenir un token write, puis s’injectait dans le code du dépôt cible. Depuis ce dépôt infecté, il ouvrait des PR vers d’autres dépôts et se propageait de manière exponentielle.

Vecteurs exploités :

  • pull_request_target + checkout du code du fork
  • GITHUB_TOKEN avec contents: write
  • Tokens d’organisation à portée large
  • Absence de contrôle réseau (exfiltration libre)

Un bot IA autonome qui a ciblé 6 dépôts majeurs de l’écosystème open-source entre le 21 et le 28 février 2026. Le bot identifiait automatiquement les workflows vulnérables, créait des PRs malveillantes, et exploitait pull_request_target pour obtenir une exécution de code avec les privilèges du dépôt cible.

Sur Trivy, l’attaque a conduit au vidage complet du dépôt GitHub et à la suppression de toutes les releases de la v0.27.0 à la v0.69.1.

Vecteurs exploités :

  • pull_request_target sans garde-fous
  • GITHUB_TOKEN avec permissions d’écriture
  • Workflow qui exécutait du code provenant du fork

Pour le récit complet de l’incident, voir le billet Trivy vidé après une attaque supply chain par un bot IA.

En analysant ces attaques, 5 vecteurs reviennent systématiquement. Les corriger bloque la quasi-totalité des attaques connues.

Vecteur 1 — La Pwn Request (pull_request_target)

Section intitulée « Vecteur 1 — La Pwn Request (pull_request_target) »

C’est le vecteur le plus dévastateur. L’événement pull_request_target a été conçu pour permettre aux mainteneurs de réagir aux PRs de forks avec accès aux secrets (par exemple, pour labelliser). Le problème : il exécute le workflow dans le contexte du dépôt cible, avec ses secrets et un GITHUB_TOKEN potentiellement en écriture.

ÉvénementCode exécutéAccès aux secretsRisque
pull_requestCode du forkNon (forks)Faible
pull_request_targetCode de la branche cibleOuiCritique si checkout du fork

Le scénario d’exploitation :

  1. L’attaquant fork le dépôt cible

  2. Il modifie un script (build, test, linter) pour injecter du code malveillant

  3. Il ouvre une PR — ce qui déclenche pull_request_target

  4. Le workflow checkout le code de la PR (ref: ${{ github.event.pull_request.head.sha }}) et l’exécute avec les privilèges du dépôt cible

  5. Le code malveillant exfiltre les secrets, modifie le code, ou se propage

Comment se protéger :

# ❌ DANGEREUX : checkout du code du fork avec accès aux secrets
on:
pull_request_target:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- run: npm test # Exécute le code du fork avec les secrets du repo
# ✅ SÉCURISÉ : utiliser pull_request (pas d'accès aux secrets pour les forks)
on:
pull_request:
types: [opened, synchronize]
jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: npm test

Si vous avez absolument besoin de pull_request_target (rare), appliquez ces garde-fous :

  • Ne faites jamais de checkout du code de la PR
  • Ajoutez une condition if restrictive (label spécifique, auteur autorisé)
  • Limitez les permissions au strict minimum

Guide détaillé : Sécuriser pull_request_target (en cours d’écriture).

Vecteur 2 — Tags mutables et supply chain des actions

Section intitulée « Vecteur 2 — Tags mutables et supply chain des actions »

Quand vous écrivez uses: some/action@v1, GitHub résout le tag vers un commit. Mais un tag est une étiquette mobile : le mainteneur (ou un attaquant) peut le déplacer vers n’importe quel commit à tout moment.

C’est le mécanisme exact exploité par tj-actions/changed-files : les tags @v1, @v2, etc. ont été réécrits pour pointer vers du code qui exfiltrait les secrets.

Comment se protéger :

# ❌ Tag mutable — le contenu peut changer sans préavis
- uses: actions/checkout@v4
# ❌ Version précise — mais le tag reste mutable
- uses: actions/checkout@v4.2.2
# ✅ SHA complet — immuable et vérifiable
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
  1. Convertir tous vos tags en SHA

    Utilisez StepSecurity (interface web) ou l’outil CLI :

    Fenêtre de terminal
    npm install -g pin-github-action
    pin-github-action .github/workflows/ci.yml
  2. Activer Dependabot pour les mises à jour automatiques

    .github/dependabot.yml
    version: 2
    updates:
    - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
    interval: "weekly"
  3. Auditer avec Scorecard

    Fenêtre de terminal
    scorecard --local . --checks Pinned-Dependencies --show-details

Guide détaillé : Épingler les actions par SHA.

Le GITHUB_TOKEN est généré automatiquement pour chaque workflow. Avec des permissions trop larges, un workflow compromis peut modifier le code source, créer des releases, publier des packages, et persister dans le dépôt.

C’est un accélérateur de toutes les autres attaques. Comme le souligne l’analyse d’Arctiq, combiné avec pull_request_target, c’est une “combinaison explosive” utilisée par Shai Hulud v2 et GhostAction.

Comment se protéger :

# Permissions zéro par défaut au niveau workflow
permissions: {}
jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read # Seulement ce qui est nécessaire
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: npm test
publish:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # Write uniquement pour le job qui publie
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: npm publish

Guide détaillé : Permissions GitHub Actions (en cours d’écriture).

L’interpolation ${{ }} dans un bloc run: se fait avant l’exécution du shell, sans échappement. Un attaquant qui contrôle un titre de PR, un nom de branche ou un corps d’issue peut injecter du code arbitraire.

L’attaque Gluestack/gluestack-ui (juin 2025) exploitait exactement ce vecteur : une commande malveillante injectée via la page de discussions du dépôt, déclenchée par un workflow qui incorporait des inputs non validés dans un bloc run:.

Données dangereuses (ne jamais interpoler directement) :

SourceVariable
Titre de PRgithub.event.pull_request.title
Corps de PRgithub.event.pull_request.body
Titre d’issuegithub.event.issue.title
Commentairegithub.event.comment.body
Nom de branchegithub.event.pull_request.head.ref
Message de commitgithub.event.head_commit.message
Input dispatchgithub.event.inputs.*

Comment se protéger :

# ❌ Injection possible
- run: echo "PR: ${{ github.event.pull_request.title }}"
# ✅ Variable d'environnement — le shell traite la valeur comme une chaîne
- run: echo "PR: $PR_TITLE"
env:
PR_TITLE: ${{ github.event.pull_request.title }}

Vecteur 5 — Exfiltration réseau non contrôlée

Section intitulée « Vecteur 5 — Exfiltration réseau non contrôlée »

Tous les vecteurs précédents ont besoin d’un canal de sortie pour envoyer les secrets volés. Si vous bloquez les connexions réseau sortantes, même un workflow compromis ne peut pas exfiltrer vos données.

Comment se protéger :

steps:
# DOIT être la première étape du job
- uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.10.2
with:
egress-policy: audit # D'abord observer, puis bloquer
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: npm ci && npm test
  1. Déployer en mode audit pour observer les connexions légitimes

  2. Analyser le tableau de bord StepSecurity (lien dans les logs du job)

  3. Passer en mode block avec la liste des endpoints autorisés

    - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6
    with:
    egress-policy: block
    allowed-endpoints: >
    github.com:443
    registry.npmjs.org:443
    objects.githubusercontent.com:443

Aucune mesure isolée ne suffit. La sécurité de vos workflows repose sur la combinaison de plusieurs couches de protection :

┌─────────────────────────────────────────────┐
│ Couche 1 — Prévention (shift left) │
│ actionlint, poutine, Semgrep, pre-commit │
├─────────────────────────────────────────────┤
│ Couche 2 — Configuration │
│ permissions: {}, SHA pinning, OIDC │
├─────────────────────────────────────────────┤
│ Couche 3 — Détection │
│ Harden Runner, Gitleaks, Scorecard │
├─────────────────────────────────────────────┤
│ Couche 4 — Confinement │
│ egress: block, runners éphémères, env gate │
├─────────────────────────────────────────────┤
│ Couche 5 — Réponse │
│ Rotation des secrets, audit logs, SIEM │
└─────────────────────────────────────────────┘

Couche 1 — Prévention : détecter les problèmes avant le commit

Section intitulée « Couche 1 — Prévention : détecter les problèmes avant le commit »

Intégrez des linters et scanners dans vos hooks pre-commit et votre CI :

OutilCe qu’il détecte
actionlintErreurs de syntaxe, patterns dangereux
poutineVulnérabilités spécifiques GitHub Actions
SemgrepRègles custom (ban pull_request_target, etc.)
CheckovMauvaises pratiques dans les workflows

Couche 2 — Configuration : réduire la surface d’attaque

Section intitulée « Couche 2 — Configuration : réduire la surface d’attaque »

Ce sont les mesures décrites dans les 5 vecteurs ci-dessus :

  • permissions: {} au niveau workflow
  • Épinglage SHA systématique
  • OIDC pour les déploiements cloud
  • Secrets scopés par step et environnement

Couche 3 — Détection : surveiller en temps réel

Section intitulée « Couche 3 — Détection : surveiller en temps réel »
  • Harden Runner : surveille les connexions réseau pendant l’exécution
  • Gitleaks / TruffleHog : scan continu des commits pour les secrets
  • Scorecard : audit périodique de la posture sécurité

Couche 4 — Confinement : limiter le rayon d’explosion

Section intitulée « Couche 4 — Confinement : limiter le rayon d’explosion »
  • egress: block : empêcher l’exfiltration réseau
  • Runners éphémères : pas de persistance entre les jobs
  • Environnements avec approbation : gate humain avant la production
  • Rotation des secrets : procédure documentée et testée
  • Audit logs : centraliser dans un SIEM pour investigation
  • Dependabot : alertes automatiques sur les actions vulnérables

Si vous partez de zéro, voici par où commencer :

  1. Auditer vos workflows existants

    Fenêtre de terminal
    # Lister tous les workflows
    find .github/workflows -name '*.yml' -o -name '*.yaml'
    # Chercher les patterns dangereux
    grep -rn 'pull_request_target' .github/workflows/
    grep -rn 'permissions:.*write-all' .github/workflows/
    grep -rn '\${{.*github\.event\.' .github/workflows/
    grep -rn '@v[0-9]' .github/workflows/
    # Audit complet avec Scorecard
    scorecard --local . --format json | jq '.checks[] | {name, score}'
  2. Corriger les failles critiques en priorité

    • Supprimer tout pull_request_target avec checkout du fork
    • Ajouter permissions: {} à chaque workflow
    • Remplacer ${{ github.event.* }} dans les run: par des env:
  3. Épingler toutes les actions par SHA

    Fenêtre de terminal
    npm install -g pin-github-action
    find .github/workflows -name '*.yml' -exec pin-github-action {} \;
  4. Ajouter Harden Runner et Gitleaks

    Déployer Harden Runner en mode audit sur tous les jobs, et Gitleaks pour bloquer le merge si un secret est détecté.

  5. Mettre en place le suivi continu

    Activer Dependabot pour github-actions, planifier un audit Scorecard mensuel, et configurer les alertes dans votre SIEM.

Les attaques supply chain sur GitHub Actions exploitent toujours les mêmes 5 vecteurs : pull_request_target mal sécurisé, tags mutables, permissions excessives, injection de commandes et exfiltration réseau non contrôlée.

Les attaques de 2025-2026 (Shai Hulud v2, GhostAction, tj-actions, hackerbot-claw) montrent que ces failles sont activement exploitées à grande échelle, y compris par des bots IA autonomes.

La bonne nouvelle : corriger ces 5 vecteurs ne nécessite pas d’outils sophistiqués. C’est une question de discipline — permissions minimales, SHA partout, inputs jamais interpolés, secrets isolés, réseau surveillé.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn