tadata
← Retour à l'accueil

Sécuriser la CI/CD et la chaîne d'approvisionnement logicielle : guide pratique GitHub Actions

#security#ci-cd#supply-chain#devops#cloud

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

MaillonAttaqueCas réelContrôle
SourcePrise de contrôle d'un mainteneur, commit malveillantPorte dérobée xz utils (2024)Protection de branche, revue obligatoire, commits signés
DépendancesTyposquatting, mise à jour malveillanteevent-stream (2018), Shai-Hulud (2025)Lock files, délai de carence, analyse, proxy de registre privé
Actions CITag redirigé vers du code malveillanttj-actions (2025)Épinglage par SHA complet
BuildServeur de build compromisSolarWinds SUNBURST (2020)Builds hébergés et isolés, provenance signée
IdentifiantsClés cloud longue durée dans les secrets CIFuites innombrablesFédération OIDC, identifiants éphémères
DistributionArtefact remplacé dans le registreJetons de registre volésSignature et vérification au déploiement
tadata
  1. SolarWinds · Dec 2020 — SUNSPOT implant in the Orion build system swapped source during compilation; signed, trojaned updates
  2. Codecov · Apr 2021 — Modified Bash uploader exfiltrated CI environment variables (tokens, keys) for months
  3. Log4Shell · Dec 2021 — CVE-2021-44228: transitive dependency everywhere; organisations without SBOMs could not answer are we affected?
  4. xz-utils · Mar 2024 — CVE-2024-3094: backdoor planted by a long-term maintainer in release tarballs, targeting sshd via liblzma
  5. 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
  6. 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
  7. 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
  8. 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)
Les incidents de supply chain qui ont façonné la pratique

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 BuildL1L2L3
Build scripté, provenance généréeouiouioui
Exécution sur une plateforme de build hébergée, pas un postenonouioui
Provenance signée par la plateformenonouioui
Builds isolés les uns des autresnonnonoui
Matériel de signature inaccessible aux étapes de buildnonnonoui

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.

tadata
GitHub OIDC provider Issuer token.actions.githubusercontent.com Signs a short-lived JWT per job Job token claims aud sts.amazonaws.com sub repo:acme/api:ref:refs/heads/main issues IAM role trust policy Federated the OIDC provider ARN StringEquals token.actions... sub = repo:acme/api:ref:refs/heads/main checked by STS AssumeRoleWithWebIdentity Returns ASIA... keys, 1 h default Logged in CloudTrail with the sub claim allows
GitHub OIDC provider Issuer token.actions.githubusercontent.com Signs a short-lived JWT per job Job token claims aud sts.amazonaws.com sub repo:acme/api:ref:refs/heads/main issues IAM role trust policy Federated the OIDC provider ARN StringEquals token.actions... sub = repo:acme/api:ref:refs/heads/main checked by STS AssumeRoleWithWebIdentity Returns ASIA... keys, 1 h default Logged in CloudTrail with the sub claim allows
CI sans clé avec l'OIDC de GitHub Actions : chaque maillon vérifie le précédent, et la condition 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 aud

Extrait 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: write pour déployer, contents: write uniquement pour le job de release, packages: write uniquement pour publier).
  • pull_request_target, workflow_run et issue_comment s'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 job pull_request non 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.

AvantAprès
uses: tj-actions/changed-files@v45uses: tj-actions/changed-files@<sha-40-caracteres> # v45
uses: some/action@mainFork relu dans votre propre organisation
docker://image:latestdocker://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ôleExemples d'outilsSeuil de blocage
Lock files, installations reproductiblesnpm ci, pip install --require-hashes, go.sumÉchec du build sans lock file
Vulnérabilités des dépendancesDependabot, Renovate, Trivy, Grype, OSV-ScannerBlocage sur critique/élevée corrigeable
Délai de carence sur les nouvelles versionsRenovate minimumReleaseAge, proxy de registreLa plupart des versions malveillantes sont détectées en quelques jours
Scripts d'installationnpm ci --ignore-scripts quand c'est possibleRevue des paquets qui en ont besoin
Détection de secretsPush protection GitHub, gitleaks, TruffleHogBlocage du push ; rotation de tout ce qui a fuité
SAST et analyse IaCCodeQL, Semgrep, Checkov, tfsecBlocage 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.

tadata

Actors: CI job, OIDC issuer, Fulcio CA, Rekor log, Cluster admission

  1. CI job → OIDC issuer request workload identity token
  2. OIDC issuer ⇠ CI job signed token: repo, workflow, ref
  3. CI job ↺ ephemeral key pair in memory
  4. CI job → Fulcio CA public key + token + proof of possession
  5. Fulcio CA ⇠ CI job certificate valid ~10 min, identity in SAN
  6. CI job → Rekor log signature of the image digest + certificate
  7. Rekor log ⇠ CI job inclusion proof, signed timestamp
  8. CI job,Rekor log: Private key discarded; signature stored next to the image in the registry
  9. Cluster admission → Rekor log verify chain to Fulcio root, expected issuer + workflow identity, signing time inside the certificate window
Signature keyless Sigstore en CI (cosign) : aucune clé de signature longue durée à voler ; la confiance repose sur l'identité de la CI et sur le journal de transparence public

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é

SemaineAction
1OIDC pour tous les déploiements cloud ; suppression des clés cloud stockées
1permissions: {} par défaut ; audit des workflows pull_request_target
2Épinglage de toutes les actions par SHA ; Dependabot pour les actions
2Détection de secrets avec push protection ; rotation des constats
3-4Rulesets, CODEOWNERS sur les workflows, environnements protégés
4-6SBOM, signature cosign, attestations de provenance
6-8Vé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.

Nous utilisons des cookies analytiques pour améliorer votre expérience. Aucune donnée personnelle n'est collectée.