Sécuriser une pipeline CI/CD, c'est réduire ce qu'elle peut faire quand elle est compromise, sur quatre surfaces : les identités, les dépendances, les runners et les artefacts. Une pipeline détient des secrets vers la production, écrit les artefacts que vos utilisateurs installent, et exécute du code tiers que personne ne relit. Cette leçon s'adresse à qui écrit ou maintient des pipelines sans être spécialiste de la sécurité. Elle donne un modèle valable quel que soit l'outil, GitHub Actions, GitLab CI ou Jenkins, et renvoie aux leçons qui le mettent en œuvre.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Expliquer pourquoi une pipeline est une cible plus rentable qu'un serveur
- Identifier les quatre surfaces de contrôle et le risque propre à chacune
- Appliquer le moindre privilège, l'épinglage et l'isolation des runners
- Distinguer un contrôle structurel d'un contrôle ponctuel
- Restreindre le code tiers autorisé et tracer les changements de configuration de la CI
- Situer une pipeline dans un modèle de maturité en quatre niveaux
Pourquoi une pipeline est-elle une cible ?
Section intitulée « Pourquoi une pipeline est-elle une cible ? »Une pipeline CI/CD (intégration et livraison continues) est le système qui teste, construit et déploie votre code sans intervention humaine. Pour faire ce travail, elle reçoit des droits étendus : lire le code, publier des artefacts, se connecter à la production. Un attaquant qui la contrôle hérite de tous ces droits d'un coup, sans jamais toucher à vos serveurs.
C'est la différence avec une attaque classique. Compromettre un serveur donne accès à ce serveur ; compromettre la pipeline donne accès à tout ce qu'elle livre, à chaque exécution. L'image souvent employée est celle de la source d'eau : on n'empoisonne pas chaque verre, on empoisonne le réservoir, et le poison arrive partout.
Ce qu'un attaquant peut obtenir
Section intitulée « Ce qu'un attaquant peut obtenir »Le tableau ci-dessous relie chaque ressource que la pipeline manipule à ce qu'un attaquant en ferait. Lisez-le comme une liste de ce qu'il faut protéger : chaque ligne correspond à une surface de contrôle traitée plus bas, et la dernière colonne montre que l'impact dépasse toujours la pipeline elle-même.
| Ressource | Ce que l'attaquant peut faire | Exemple concret |
|---|---|---|
| Secrets | Voler les tokens d'API, clés SSH, identifiants cloud | Utiliser vos propres clés sur votre compte cloud |
| Code source | Modifier le code avant compilation | Ajouter une porte dérobée invisible à la revue |
| Artefacts | Remplacer un livrable | Vos utilisateurs installent une version piégée |
| Production | Déployer directement | Prendre la main sur vos serveurs |
Le cas tj-actions/changed-files, en mars 2025
Section intitulée « Le cas tj-actions/changed-files, en mars 2025 »L'attaque la plus instructive de ces dernières années s'est jouée sur une action GitHub utilisée par plus de 23 000 dépôts. Les 14 et 15 mars 2025, ses tags de version ont été redéplacés vers un commit malveillant (CVE-2025-30066). Ce code lisait la mémoire du runner, en extrayait les secrets, et les écrivait encodés en base64 dans les journaux du workflow.
Sur un dépôt public, ces journaux sont lisibles par tous : les secrets étaient donc exposés sans qu'aucun serveur externe soit contacté. Les équipes touchées n'avaient rien changé, puisqu'elles référençaient l'action par un tag, et c'est ce tag qui avait bougé sous leurs pieds. Seuls les dépôts qui épinglaient l'action par SHA n'ont pas exécuté le code piégé. Le correctif est arrivé en version 46.0.1.
Quelles sont les quatre surfaces de contrôle ?
Section intitulée « Quelles sont les quatre surfaces de contrôle ? »La sécurité d'une pipeline se joue sur quatre surfaces, et chacune correspond à une décision de confiance qui doit être explicite et vérifiable. Le tableau résume la question à se poser pour chacune et ce qui arrive quand on l'ignore. Les sections suivantes les traitent une par une, du plus simple à mettre en place au plus structurant.
| Surface | Question clé | Risque si négligée |
|---|---|---|
| Identités | Qui a le droit de faire quoi, et combien de temps ? | Accès excessifs, secrets volés |
| Dépendances | À quel code externe faisons-nous confiance ? | Code malveillant exécuté |
| Runners | Où s'exécute le code, et que garde la machine ? | Contamination entre jobs |
| Artefacts | Comment prouver l'origine du livrable ? | Distribution d'un livrable piégé |
Surface 1 : identités et permissions
Section intitulée « Surface 1 : identités et permissions »La question n'est pas de savoir si la pipeline doit s'authentifier, mais avec quoi et pour combien de temps. Elle se connecte à un registre d'images, à un fournisseur cloud, à un outil de déploiement, et chaque connexion repose sur un identifiant. Un secret de longue durée stocké dans la CI reste valable après le départ de son auteur, et après la fuite qui l'a exposé.
Le moindre privilège, job par job
Section intitulée « Le moindre privilège, job par job »Chaque job ne reçoit que les permissions strictement nécessaires à sa
tâche. Sur GitHub Actions, le jeton automatique se règle par le bloc
permissions : on le vide au niveau du workflow, puis on accorde les droits
job par job. Un job de test compromis ne peut alors ni pousser du code ni
publier un paquet.
# ❌ Permissions excessives : un job compromis peut tout fairepermissions: write-all
# Permissions minimales : rien par défaut, puis le strict nécessaire par jobpermissions: {}
jobs: publier: runs-on: ubuntu-24.04 permissions: contents: read # lire le code, sans pouvoir le modifier packages: write # publier un paquet, rien d'autreDes jetons éphémères plutôt que des secrets permanents
Section intitulée « Des jetons éphémères plutôt que des secrets permanents »Un jeton éphémère expire de lui-même après quelques minutes ; un secret statique reste valide jusqu'à ce qu'on le révoque, ce qui est souvent oublié. Le protocole OIDC (OpenID Connect) permet à la pipeline de demander au fournisseur cloud un jeton temporaire à chaque exécution, au lieu de stocker une clé permanente. Le tableau montre ce que change ce choix le jour d'une fuite.
| Caractéristique | Secret statique | Jeton éphémère (OIDC) |
|---|---|---|
| Durée de vie | Illimitée | Quelques minutes |
| Si volé | Utilisable jusqu'à détection | Expire rapidement |
| Révocation | Manuelle, souvent oubliée | Automatique |
La mise en œuvre sur GitHub Actions fait l'objet de la leçon OIDC GitHub Actions : authentification sans secrets.
Un secret par environnement
Section intitulée « Un secret par environnement »Les secrets de staging et ceux de production doivent être distincts, et rangés dans des environnements protégés de la plateforme. Une fuite côté recette ne donne alors aucun accès à la production. Avant de configurer les accès d'un job, quatre questions suffisent : ce jeton a-t-il besoin d'un accès en écriture ou d'une simple lecture ? OIDC est-il possible ? Que perd-on si ce secret est volé ? Quand a-t-il été renouvelé pour la dernière fois ?
Surface 2 : dépendances
Section intitulée « Surface 2 : dépendances »Le code que vous écrivez n'est qu'une fraction de ce qui s'exécute. Chaque
npm install, chaque pip install, chaque action tierce exécute du code
venu de mainteneurs que vous ne connaissez pas, avec les permissions de
votre pipeline. L'objectif est de rendre reproductible ce que la pipeline
installe, et détectable ce qui change.
Épingler par SHA, pas par tag
Section intitulée « Épingler par SHA, pas par tag »Un tag comme @v4 est mutable : son mainteneur, ou un attaquant qui a
pris son compte, peut le déplacer vers un autre commit. Un SHA de commit est
immuable : il désigne toujours exactement le même code. C'est précisément
ce qui a protégé les dépôts épinglés lors de l'attaque tj-actions.
# ❌ Tag mutable : peut pointer demain vers un autre code- uses: actions/checkout@v4
# SHA de commit immuable, la version en commentaire pour la lecture- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1Le SHA à retenir est celui du commit, pas celui de l'objet tag. La commande
git ls-remote --tags https://github.com/actions/checkout v7.0.1 le donne ;
pour un tag annoté, prenez la ligne suffixée ^{}, qui désigne le commit
pointé. La méthode complète est dans la leçon
Épingler les actions par SHA.
Décider quel code tiers a le droit de s'exécuter
Section intitulée « Décider quel code tiers a le droit de s'exécuter »L'épinglage répond à la question « ce code va-t-il changer ? », pas à « ai-je
le droit de l'exécuter ? ». Une liste d'autorisation posée au niveau de
l'organisation ferme la seconde : seules les actions explicitement admises
s'exécutent, quel que soit le workflow qui les appelle, y compris un workflow
ajouté par un compte compromis. Sur GitHub, la politique « autoriser
l'organisation et une sélection » accepte des motifs comme actions/* ou
sigstore/cosign-installer@*, et n'existe qu'au niveau d'une organisation.
C'est un contrôle structurel : il s'applique sans dépendre du YAML de chaque dépôt, que n'importe quel contributeur peut modifier. Les deux contrôles se complètent sans se remplacer : une action autorisée mais épinglée par tag reste exposée à une réécriture, une action épinglée mais non autorisée ne devrait pas tourner du tout. La leçon Restreindre les actions autorisées en détaille la syntaxe.
Versionner les fichiers de verrouillage
Section intitulée « Versionner les fichiers de verrouillage »Un fichier de verrouillage (package-lock.json, poetry.lock) enregistre
la version exacte de chaque dépendance, y compris des dépendances
transitives. La pipeline l'installe alors tel quel, avec npm ci plutôt
que npm install, et produit le même résultat à chaque exécution. Le
tableau compare les deux situations.
| Sans fichier de verrouillage | Avec fichier de verrouillage |
|---|---|
npm install prend les dernières versions | npm ci installe exactement les versions enregistrées |
| Résultat différent d'une exécution à l'autre | Résultat identique à chaque exécution |
| Une mise à jour arrive sans revue | Les mises à jour passent par une revue |
Auditer avant d'ajouter
Section intitulée « Auditer avant d'ajouter »Une nouvelle dépendance est une décision de confiance, pas un détail technique. Avant de l'ajouter, regardez qui la maintient, depuis quand, combien de projets l'utilisent, et si son code est lisible. Un mainteneur unique est un risque, tout comme un paquet récent ou un code obfusqué. L'outil OpenSSF Scorecard automatise une partie de ces vérifications.
| Critère | Pourquoi c'est important |
|---|---|
| Qui la maintient ? | Un mainteneur unique est un point de rupture |
| Depuis combien de temps ? | Une dépendance récente n'a pas fait ses preuves |
| Combien de projets l'utilisent ? | Peu d'utilisateurs, peu d'yeux pour repérer un problème |
| Le code est-il lisible ? | Du code obfusqué peut cacher quelque chose |
Surface 3 : runners et isolation
Section intitulée « Surface 3 : runners et isolation »Le runner est la machine, physique ou virtuelle, qui exécute vos jobs. Tout ce qu'il conserve d'une exécution à l'autre, cache, identifiants, fichiers temporaires, devient accessible au job suivant, y compris quand ce job vient d'une contribution externe. L'objectif est de supprimer cette persistance, vecteur principal de contamination d'un job à l'autre.
Des runners éphémères
Section intitulée « Des runners éphémères »Un runner éphémère est créé pour un job, puis détruit : rien ne survit à l'exécution. Un runner persistant, à l'inverse, garde l'état laissé par chaque job, et un code malveillant peut s'y installer pour attendre le prochain déploiement. Le tableau suit deux jobs successifs dans chacun des cas ; la leçon Runners éphémères GitHub Actions en donne la mise en œuvre.
| Runner persistant | Runner éphémère |
|---|---|
| Le job 1 modifie des fichiers | Le job 1 s'exécute, le runner est détruit |
| Le job 2 hérite de ces modifications | Le job 2 obtient un runner neuf |
| Un code malveillant peut persister | Rien ne peut persister |
Une isolation réseau stricte
Section intitulée « Une isolation réseau stricte »Le runner n'accède qu'aux ressources dont le job a besoin : le registre, le dépôt de paquets, l'API de déploiement. Par défaut, il ne voit ni le réseau interne ni les bases de données de l'entreprise. Un job compromis sur un runner isolé ne peut alors ni explorer le réseau ni exfiltrer des données vers une destination inattendue.
Séparer le code externe des secrets
Section intitulée « Séparer le code externe des secrets »Quand une personne extérieure ouvre une pull request depuis un fork, le
code qu'elle propose s'exécute sur vos runners. Ce code ne doit jamais
atteindre vos secrets. Sur GitHub Actions, le déclencheur pull_request
respecte cette règle : un workflow lancé depuis un fork ne reçoit ni secrets ni
jeton en écriture.
Le piège vient du déclencheur pull_request_target, qui s'exécute avec les
secrets du dépôt cible. S'il récupère et exécute le code de la pull request, il
offre ces secrets à n'importe quel contributeur. La leçon
Sécuriser pull_request_target
détaille l'attaque et la parade.
Surface 4 : artefacts et chaîne de confiance
Section intitulée « Surface 4 : artefacts et chaîne de confiance »Entre la construction d'un artefact et son déploiement, rien ne prouve par défaut qu'il s'agit du même contenu. Un artefact, c'est le résultat de la pipeline : une image de conteneur, un exécutable, un paquet. Sans empreinte vérifiable ni signature, un remplacement en cours de route est indétectable par celui qui l'installe.
Signer les artefacts
Section intitulée « Signer les artefacts »Signer un artefact, c'est y associer une preuve cryptographique de son origine : si l'artefact est modifié après la signature, la vérification échoue. Le consommateur n'a plus à faire confiance au canal de distribution, il vérifie lui-même. L'outil de référence pour les images est Cosign.
| Artefact non signé | Artefact signé |
|---|---|
| Origine inconnue | Origine vérifiable |
| Intégrité incertaine | Toute modification est détectée |
| « Faites-moi confiance » | « Vérifiez vous-même » |
Attestations de provenance et SBOM
Section intitulée « Attestations de provenance et SBOM »Une attestation de provenance décrit comment l'artefact a été construit : quel commit, quelle pipeline, quel environnement. Le cadre SLSA en fixe les niveaux de garantie. Un SBOM (Software Bill of Materials) est la liste complète des composants de l'artefact, à la manière d'une liste d'ingrédients.
Ces trois preuves répondent à trois questions différentes. La signature dit qui a produit l'artefact, l'attestation dit comment, et le SBOM dit ce qu'il contient. Le jour où une vulnérabilité comme Log4Shell est publiée, le SBOM vous dit en quelques secondes quels artefacts sont concernés.
Contrôle structurel ou contrôle ponctuel : lequel privilégier ?
Section intitulée « Contrôle structurel ou contrôle ponctuel : lequel privilégier ? »Un contrôle structurel rend une action impossible par construction ; un contrôle ponctuel la vérifie à un instant donné. Le premier est plus solide, parce qu'il ne dépend ni d'une étape qu'on peut retirer ni d'une alerte qu'on peut ignorer. Un scan de vulnérabilités, une analyse statique ou un test de sécurité sur une pull request sont des contrôles ponctuels : utiles, mais contournables, désactivables, ou simplement oubliés.
Le tableau donne cinq contrôles structurels et ce qu'ils rendent impossible. Les contrôles ponctuels restent des filets supplémentaires, pas des remparts. La leçon Bloquer une pipeline n'améliore pas la sécurité montre ce qui se passe quand on attend d'eux plus qu'ils ne peuvent donner.
| Contrôle structurel | Ce qu'il rend impossible |
|---|---|
| Permissions minimales par défaut | Obtenir plus de droits que prévu |
| Runners éphémères | Faire persister un code malveillant |
| Signature exigée au déploiement | Déployer un artefact non vérifié |
| OIDC au lieu de secrets statiques | Voler un identifiant permanent |
| Liste d'autorisation des actions | Exécuter une action tierce non approuvée |
Qui a modifié la pipeline ? Le journal d'audit
Section intitulée « Qui a modifié la pipeline ? Le journal d'audit »L'historique Git trace les fichiers de workflow, pas les réglages de la plateforme. Un secret remplacé, une politique d'actions assouplie, un runner auto-hébergé ajouté à l'organisation : rien de cela n'apparaît dans un commit. Ces changements vivent dans le journal d'audit de la plateforme, et c'est lui qui répond, le jour d'un incident, à la question « qui a desserré ce contrôle, et quand ? ».
Le journal d'audit n'empêche rien : c'est un contrôle de détection et d'investigation, qui ne vaut que s'il est consulté. Sur GitHub, quatre événements résument ce qu'une équipe sécurité doit surveiller sur la CI :
| Ce qui change | Événement GitHub |
|---|---|
| La politique Actions de l'organisation | org.update_actions_settings |
| Un secret Actions d'organisation | org.update_actions_secret |
| Un nouveau runner auto-hébergé | org.register_self_hosted_runner |
| La configuration d'un groupe de runners | org.runner_group_updated |
Deux limites, relevées dans la documentation GitHub, décident de la façon de l'exploiter. Seuls les propriétaires de l'organisation y accèdent, et l'interface ne remonte que les 180 derniers jours. L'accès par API, qui permet de l'exporter vers un SIEM, est réservé à GitHub Enterprise Cloud : sans lui, la conservation au-delà de six mois suppose un export manuel régulier. Côté GitLab, les audit events jouent le même rôle, comme le détaille la leçon GitLab CI : audit, conformité et traçabilité.
Où en est votre pipeline ? Un modèle de maturité en quatre niveaux
Section intitulée « Où en est votre pipeline ? Un modèle de maturité en quatre niveaux »Ce modèle sert à situer une pipeline et à choisir la prochaine étape, pas à tout faire d'un coup. Chaque niveau suppose le précédent : signer des artefacts construits sur un runner persistant aux permissions larges ne protège pas grand-chose. Commencez par l'hygiène de base, qui coûte peu et ferme les attaques les plus fréquentes.
| Niveau | Objectif | Contrôles |
|---|---|---|
| 1. Hygiène de base | Fermer les attaques les plus fréquentes | Permissions minimales par job, fichiers de verrouillage avec npm ci, aucun secret en dur |
| 2. Épinglage et isolation | Ne rien exécuter qui puisse changer seul | Actions et dépendances épinglées par SHA, liste d'autorisation des actions, runners éphémères, un secret par environnement |
| 3. Vérification et traçabilité | Prouver ce qui est livré | Artefacts signés, SBOM à chaque build, attestations de provenance, journal d'audit consulté et conservé |
| 4. Confiance zéro | Ne plus stocker d'identifiant durable | OIDC partout où c'est possible, vérification obligatoire avant déploiement, surveillance des runners |
À retenir
Section intitulée « À retenir »- Une pipeline compromise donne accès aux secrets, au code, aux artefacts et à la production, à chaque exécution.
- Les quatre surfaces de contrôle sont les identités, les dépendances, les runners et les artefacts.
- Moindre privilège : permissions vidées au niveau du workflow, puis accordées job par job.
- Épinglez par SHA de commit : l'attaque tj-actions de mars 2025 a déplacé des tags, pas des SHA.
- Runners éphémères et isolation réseau suppriment la persistance d'un job à l'autre.
- Signature, provenance et SBOM disent qui a produit l'artefact, comment, et ce qu'il contient.
- Privilégiez les contrôles structurels, qui rendent l'attaque impossible, aux contrôles ponctuels, qui la constatent.
- Une liste d'autorisation d'organisation décide quel code tiers s'exécute ; le journal d'audit dit qui a modifié les réglages que Git ne voit pas.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de cette leçon est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Anti-patterns CI/CD : reconnaître et corriger les erreurs de pipeline
Continuer le cours : Les secrets dans les pipelines CI/CD
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications