Sécuriser la CI/CD et la chaîne d'approvisionnement logicielle : guide pratique GitHub Actions
Un pipeline CI/CD exécute du code écrit par de nombreuses personnes tout en détenant les clés de la production. C'est donc l'une des cibles les plus précieuses d'une organisation. En mars 2025, les tags d'une GitHub Action populaire, tj-actions/changed-files, ont été redirigés vers un code malveillant qui déversait les secrets dans les journaux de build ; environ 23 000 dépôts l'utilisaient. Des vers comme Shai-Hulud (2025) se sont propagés via des jetons npm volés. Aucune de ces attaques n'a eu besoin d'une faille inédite : elles ont exploité des références mutables, des identifiants longue durée et des jobs trop privilégiés. Ce guide présente les contrôles qui les auraient arrêtées, avec des exemples GitHub Actions concrets. Pour le cadre général (formats SBOM, VEX, Scorecard), voir notre panorama de la sécurité de la supply chain.
Où la chaîne casse
| Maillon | Attaque | Cas réel | Contrôle |
|---|---|---|---|
| Source | Prise de contrôle d'un mainteneur, commit malveillant | Porte dérobée xz utils (2024) | Protection de branche, revue obligatoire, commits signés |
| Dépendances | Typosquatting, mise à jour malveillante | event-stream (2018), Shai-Hulud (2025) | Lock files, délai de carence, analyse, proxy de registre privé |
| Actions CI | Tag redirigé vers du code malveillant | tj-actions (2025) | Épinglage par SHA complet |
| Build | Serveur de build compromis | SolarWinds SUNBURST (2020) | Builds hébergés et isolés, provenance signée |
| Identifiants | Clés cloud longue durée dans les secrets CI | Fuites innombrables | Fédération OIDC, identifiants éphémères |
| Distribution | Artefact remplacé dans le registre | Jetons de registre volés | Signature et vérification au déploiement |
- SolarWinds · Dec 2020 — SUNSPOT implant in the Orion build system swapped source during compilation; signed, trojaned updates
- Codecov · Apr 2021 — Modified Bash uploader exfiltrated CI environment variables (tokens, keys) for months
- Log4Shell · Dec 2021 — CVE-2021-44228: transitive dependency everywhere; organisations without SBOMs could not answer are we affected?
- xz-utils · Mar 2024 — CVE-2024-3094: backdoor planted by a long-term maintainer in release tarballs, targeting sshd via liblzma
- tj-actions/changed-files · Mar 2025 — CVE-2025-30066: popular GitHub Action retagged to leak CI secrets in logs; pin actions to a commit SHA
- Nx 's1ngularity' · Aug 2025 — Malicious Nx releases published with a stolen CI token; the payload drove locally installed AI coding CLIs to hunt for secrets and wallets
- chalk / debug · Sep 2025 — Maintainer phished through a fake npm support domain; 18 packages with billions of weekly downloads shipped a crypto-wallet drainer for about two hours
- Shai-Hulud · Sep 2025 — Self-replicating npm worm: stole tokens with a secrets scanner, then republished every package the victim could publish (hundreds of packages)
Extrait de la fiche « DevSecOps part 2: supply chain and CI/CD hardening » · Demander la fiche complète
Les niveaux SLSA : une cible
SLSA (Supply-chain Levels for Software Artifacts, OpenSSF) gradue la confiance que l'on peut accorder à la provenance d'un artefact. La version 1.0 a conservé un volet Build à trois niveaux ; la version 1.2 (fin 2025) ajoute un volet Source.
| Exigence du volet Build | L1 | L2 | L3 |
|---|---|---|---|
| Build scripté, provenance générée | oui | oui | oui |
| Exécution sur une plateforme de build hébergée, pas un poste | non | oui | oui |
| Provenance signée par la plateforme | non | oui | oui |
| Builds isolés les uns des autres | non | non | oui |
| Matériel de signature inaccessible aux étapes de build | non | non | oui |
Les runners hébergés par GitHub avec les attestations d'artefacts amènent rapidement la plupart des équipes au niveau Build L2 ; le L3 exige des builds isolés dont la provenance ne peut pas être falsifiée par une étape malveillante (par exemple des workflows réutilisables qui portent la signature). La provenance prouve où et comment un artefact a été construit, pas que le code est sain : il faut la compléter par des contrôles côté source.
1. Remplacer les clés cloud longue durée par la fédération OIDC
Les secrets AWS_ACCESS_KEY_ID stockés en CI sont le chemin le plus courant entre une compromission de la CI et une compromission du cloud. Avec OIDC, la plateforme CI signe un jeton éphémère décrivant le job (dépôt, branche, environnement), que le cloud échange contre des identifiants temporaires. Rien à voler, rien à faire tourner.
name: deploy
on:
push:
branches: [main]
permissions: {} # rien par défaut
jobs:
deploy:
runs-on: ubuntu-latest
environment: prod # approbateurs requis, main uniquement
permissions:
contents: read
id-token: write # demander le jeton OIDC
steps:
- uses: actions/checkout@<sha-40-caracteres> # v4
with:
persist-credentials: false
- uses: aws-actions/configure-aws-credentials@<sha-40-caracteres> # v4
with:
role-to-assume: arn:aws:iam::123456789012:role/deploy-prod
aws-region: eu-west-3
La décision de confiance se prend côté cloud. Épinglez l'audience et le sujet exact :
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:acme/app:environment:prod"
}
}
N'utilisez jamais StringLike avec repo:acme/* : tous les dépôts, branches et pull requests de l'organisation pourraient endosser le rôle. Azure (identifiants fédérés sur une identité managée) et GCP (Workload Identity Federation) suivent le même modèle. Plus de détails sur les identités cloud dans notre guide IAM.
sub de la trust policy du rôle est le contrôle qui compte. Un sujet générique comme repo:acme/* permet à n'importe quel dépôt ou branche de l'organisation d'assumer le rôle de déploiement ; épinglez aussi audExtrait de la fiche « AWS Security Essentials » · Demander la fiche complète
2. Jetons de job au moindre privilège et déclencheurs sûrs
- Permissions par défaut des workflows en lecture au niveau de l'organisation, et
permissions: {}en tête de chaque workflow ; accordez par job (id-token: writepour déployer,contents: writeuniquement pour le job de release,packages: writeuniquement pour publier). pull_request_target,workflow_runetissue_comments'exécutent avec les secrets et un jeton en écriture. N'y faites jamais de checkout ni de build du code de la pull request ; séparez en un jobpull_requestnon privilégié et un job privilégié qui ne fait que lire des artefacts.- Traitez les champs d'événement (titres de PR, noms de branche, commentaires) comme des entrées non fiables : passez-les par des variables d'environnement, ne les interpolez jamais dans les scripts
run:. - Exigez une approbation pour les workflows des contributeurs externes.
3. Épingler chaque action tierce par SHA complet
Les tags et branches peuvent être déplacés ; un SHA de commit de 40 caractères, non.
| Avant | Après |
|---|---|
uses: tj-actions/changed-files@v45 | uses: tj-actions/changed-files@<sha-40-caracteres> # v45 |
uses: some/action@main | Fork relu dans votre propre organisation |
docker://image:latest | docker://image@sha256:<digest> |
GitHub peut imposer l'épinglage par SHA via la politique des actions autorisées (au niveau de l'organisation ou du dépôt), et Dependabot (package-ecosystem: github-actions) maintient les épinglages à jour. Auditez les workflows existants avec un linter open source comme zizmor, qui signale les actions non épinglées, les permissions excessives et les injections de modèle.
4. Analyse des dépendances et des secrets
| Contrôle | Exemples d'outils | Seuil de blocage |
|---|---|---|
| Lock files, installations reproductibles | npm ci, pip install --require-hashes, go.sum | Échec du build sans lock file |
| Vulnérabilités des dépendances | Dependabot, Renovate, Trivy, Grype, OSV-Scanner | Blocage sur critique/élevée corrigeable |
| Délai de carence sur les nouvelles versions | Renovate minimumReleaseAge, proxy de registre | La plupart des versions malveillantes sont détectées en quelques jours |
| Scripts d'installation | npm ci --ignore-scripts quand c'est possible | Revue des paquets qui en ont besoin |
| Détection de secrets | Push protection GitHub, gitleaks, TruffleHog | Blocage du push ; rotation de tout ce qui a fuité |
| SAST et analyse IaC | CodeQL, Semgrep, Checkov, tfsec | Blocage sur les nouveaux constats élevés |
Un secret poussé est compromis à l'instant même : la solution est la rotation, pas la réécriture de l'historique. Notre guide de gestion des secrets traite des coffres et de la rotation.
5. SBOM et signature avec Sigstore
Générez un SBOM pour chaque image (CycloneDX ou SPDX, par exemple avec Syft ou Trivy), signez l'image et joignez SBOM et provenance sous forme d'attestations, toutes liées au digest de l'image :
syft registry.example.com/app@sha256:<digest> -o cyclonedx-json > sbom.cdx.json
cosign sign --yes registry.example.com/app@sha256:<digest>
cosign attest --yes --type cyclonedx --predicate sbom.cdx.json \
registry.example.com/app@sha256:<digest>
En mode keyless, cosign obtient un certificat éphémère pour l'identité OIDC du workflow (Fulcio) et inscrit la signature dans un journal de transparence public (Rekor). Aucune clé privée à protéger. La vérification doit épingler l'identité :
cosign verify registry.example.com/app@sha256:<digest> \
--certificate-identity https://github.com/acme/app/.github/workflows/release.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
Appliquez la même vérification au déploiement avec un contrôleur d'admission (voir le durcissement Kubernetes). Le Cyber Resilience Act européen rend cette pratique incontournable pour les fabricants de produits : ses obligations de signalement des vulnérabilités et incidents s'appliquent depuis le 11 septembre 2026, ses exigences de SBOM et de développement sécurisé à partir du 11 décembre 2027.
Actors: CI job, OIDC issuer, Fulcio CA, Rekor log, Cluster admission
- CI job → OIDC issuer request workload identity token
- OIDC issuer ⇠ CI job signed token: repo, workflow, ref
- CI job ↺ ephemeral key pair in memory
- CI job → Fulcio CA public key + token + proof of possession
- Fulcio CA ⇠ CI job certificate valid ~10 min, identity in SAN
- CI job → Rekor log signature of the image digest + certificate
- Rekor log ⇠ CI job inclusion proof, signed timestamp
- CI job,Rekor log: Private key discarded; signature stored next to the image in the registry
- Cluster admission → Rekor log verify chain to Fulcio root, expected issuer + workflow identity, signing time inside the certificate window
Extrait de la fiche « DevSecOps part 2: supply chain and CI/CD hardening » · Demander la fiche complète
6. Protection des branches et paramètres des dépôts
- Rulesets sur la branche par défaut : pull request obligatoire, au moins une approbation, contrôles de statut requis, ni force push ni suppression.
- CODEOWNERS sur
.github/workflows/**pour que l'équipe plateforme relise les modifications de pipeline. - Environnements staging et production avec approbateurs requis et prevent self-review ; secrets limités à l'environnement.
- Décocher Allow GitHub Actions to create and approve pull requests.
- MFA (idéalement des passkeys) pour chaque membre ; jetons à granularité fine et à expiration plutôt que les PAT classiques.
- Workflows de déploiement réutilisables, portés par une équipe centrale et appelés par SHA.
Ordre de priorité
| Semaine | Action |
|---|---|
| 1 | OIDC pour tous les déploiements cloud ; suppression des clés cloud stockées |
| 1 | permissions: {} par défaut ; audit des workflows pull_request_target |
| 2 | Épinglage de toutes les actions par SHA ; Dependabot pour les actions |
| 2 | Détection de secrets avec push protection ; rotation des constats |
| 3-4 | Rulesets, CODEOWNERS sur les workflows, environnements protégés |
| 4-6 | SBOM, signature cosign, attestations de provenance |
| 6-8 | Vérification des signatures à l'admission ; Scorecard sur les dépôts critiques |
Pour la conception des pipelines au-delà de la sécurité, voir les bonnes pratiques CI/CD et les patterns GitOps.
Notre accompagnement
La sécurité de la CI/CD et de la chaîne d'approvisionnement logicielle, avec l'IaC et la gestion des secrets, fait partie de l'offre sécurité cloud et DevSecOps de Tadata : nous auditons les pipelines, mettons en place la fédération OIDC et la signature, et vous laissons des politiques sous forme de code. Voir notre page cybersécurité ; la bibliothèque de fiches contient des fiches sur la sécurité des pipelines CI/CD et de la supply chain.