En décembre 2021, la vulnérabilité Log4Shell a exposé une réalité inconfortable : la plupart des organisations ne savaient pas si elles utilisaient Log4j. La bibliothèque était présente dans des milliers d’applications, souvent comme dépendance transitive invisible. Les équipes ont passé des semaines à auditer manuellement leurs systèmes pendant que les attaquants exploitaient la faille.
Cette crise a révélé un problème structurel : nous ne savons pas ce qui compose nos logiciels. La supply chain logicielle — l’ensemble des composants, processus et personnes impliqués dans la création d’un logiciel — est devenue le maillon faible de la sécurité.
Qu’est-ce que la supply chain logicielle ?
Section intitulée « Qu’est-ce que la supply chain logicielle ? »Analogie : Imaginez la construction d’une voiture. Vous ne fabriquez pas chaque vis, chaque boulon, chaque composant électronique. Vous les achetez à des fournisseurs. Si l’un d’eux vous livre des freins défectueux, votre voiture sera dangereuse, même si votre assemblage est parfait.
En développement logiciel, c’est identique. La supply chain logicielle est l’ensemble des fournisseurs, outils et processus qui interviennent entre votre première ligne de code et l’application déployée en production. Chaque maillon peut être compromis, et une seule faille suffit à contaminer l’ensemble.
Les maillons de la chaîne
Section intitulée « Les maillons de la chaîne »Voici les composants typiques d’une supply chain logicielle, avec les risques spécifiques à chaque étape :
| Composant | Exemples | Risques associés |
|---|---|---|
| Code source | Votre code, contributions externes | Injection de code malveillant |
| Dépendances | npm, PyPI, Maven, Go modules | CVE, typosquatting, compte compromis |
| Outils de build | Compilateurs, bundlers, CI/CD | Compromission du processus de build |
| Artefacts | Images Docker, binaires, packages | Tampering, substitution |
| Infrastructure | Registries, CDN, serveurs de déploiement | Man-in-the-middle, compromission |
Chaque ligne de ce tableau représente un point d’entrée potentiel pour un attaquant. Une dépendance compromise peut exfiltrer vos secrets. Un outil de build modifié peut injecter un backdoor. Une infrastructure piratée peut distribuer une version corrompue de votre application.
L’ampleur du problème
Section intitulée « L’ampleur du problème »Les chiffres donnent le vertige :
- Une application Node.js moyenne a 600+ dépendances (directes et transitives)
- 80% du code d’une application moderne vient de dépendances tierces
- En 2023, 245 000 packages malveillants ont été détectés sur les registries publics
- Le temps moyen pour détecter un package compromis : 430 jours
Ce que ces chiffres signifient concrètement : Quand vous écrivez une application de 10 000 lignes de code, vous faites en réalité confiance à 40 000 lignes de code écrites par des inconnus. Si l’un de ces auteurs voit son compte npm compromis, votre application peut devenir un vecteur d’attaque. Et pire : vous ne le découvrirez probablement qu’un an plus tard, quand le malware sera déjà en production.
C’est exactement ce qui s’est passé avec SolarWinds, Codecov, et des dizaines d’autres incidents.
Les trois piliers de la sécurité supply chain
Section intitulée « Les trois piliers de la sécurité supply chain »Face à la complexité de la supply chain, trois questions fondamentales émergent. Si vous ne pouvez pas y répondre avec certitude, vous êtes vulnérable.
Premier réflexe : Face à une nouvelle CVE critique (comme Log4Shell), pouvez-vous identifier en moins de 10 minutes tous les systèmes affectés ? Si la réponse est “non”, c’est un problème de transparence.
Deuxième réflexe : Quand vous déployez une image Docker en production, êtes-vous sûr qu’elle n’a pas été modifiée entre le build et le déploiement ? Si vous ne pouvez pas le prouver, c’est un problème d’intégrité.
Troisième réflexe : Pouvez-vous affirmer que votre artefact provient exactement du commit Git que vous pensez, construit par votre CI/CD officiel ? Si vous devez fouiller dans les logs pour vérifier, c’est un problème de provenance.
Voici comment ces trois piliers se matérialisent techniquement :
| Pilier | Question | Solution |
|---|---|---|
| Transparence | Qu’est-ce qui compose mon logiciel ? | SBOM |
| Intégrité | Mon logiciel a-t-il été modifié ? | Signatures, SLSA |
| Provenance | D’où vient mon logiciel ? | Attestations, logs de transparence |
Pourquoi ces trois piliers sont indissociables : Un SBOM (transparence) sans signature (intégrité) peut être falsifié par un attaquant. Une signature sans SBOM ne vous dit pas si la dépendance vulnérable est présente. Une attestation de provenance sans les deux ne prouve rien sur le contenu réel de l’artefact. C’est l’intersection des trois qui crée la sécurité.
SBOM : l’inventaire des composants
Section intitulée « SBOM : l’inventaire des composants »Analogie : Quand vous achetez un produit alimentaire, vous lisez la liste des ingrédients. Farine, eau, sel, conservateurs… Si demain un conservateur est déclaré cancérigène, le fabricant peut rappeler tous les produits qui en contiennent. Sans cette liste, impossible de savoir quels produits sont concernés.
Un SBOM (Software Bill of Materials) est exactement cette liste d’ingrédients, mais pour le logiciel. C’est l’inventaire exhaustif de tous les composants qui composent votre application :
- Dépendances directes : les bibliothèques que vous avez explicitement ajoutées (
npm install express) - Dépendances transitives : les bibliothèques utilisées par vos dépendances (express dépend de 50+ packages)
- Versions exactes : pas “lodash 4.x”, mais “lodash 4.17.21”
- Licences : MIT, Apache 2.0, GPL… (crucial pour la conformité juridique)
- Hashes : empreintes cryptographiques pour vérifier l’intégrité
Pourquoi un SBOM est essentiel
Section intitulée « Pourquoi un SBOM est essentiel »Sans SBOM, vous êtes aveugle. Quand une vulnérabilité comme Log4Shell est annoncée, vous devez pouvoir répondre en minutes, pas en semaines :
| Question | Sans SBOM | Avec SBOM |
|---|---|---|
| Suis-je affecté par CVE-2024-XXXX ? | ”Je ne sais pas, il faut vérifier” | Requête automatique : oui/non |
| Quelles applications utilisent OpenSSL < 3.0 ? | Audit manuel de tous les projets | Export filtré instantané |
| Quelles licences sont présentes ? | Analyse juridique coûteuse | Liste générée automatiquement |
Formats de SBOM
Section intitulée « Formats de SBOM »Un SBOM, c’est comme un format de facture : tout le monde pourrait inventer le sien, mais pour que les systèmes se parlent, il faut des standards. Deux formats se sont imposés, chacun avec son histoire et ses forces.
CycloneDX vient du monde de la sécurité applicative (OWASP, les gens qui maintiennent le Top 10 des vulnérabilités web). Il est optimisé pour répondre rapidement à une CVE : métadonnées de sécurité riches, support natif des VEX (Vulnerability Exploitability eXchange, pour dire “oui la CVE existe, mais elle n’est pas exploitable dans mon contexte”).
SPDX vient du monde de l’open source Linux Foundation. Il est standardisé ISO et se concentre sur la conformité juridique : qui a écrit quoi, sous quelle licence, avec quelles obligations. Si votre entreprise doit prouver qu’elle respecte les licences GPL, SPDX est votre allié.
| Format | Origine | Forces | Cas d’usage |
|---|---|---|---|
| CycloneDX | OWASP | Riche en métadonnées de sécurité, VEX intégré | Sécurité, vulnérabilités |
| SPDX | Linux Foundation | Standard ISO, focus licences | Conformité, juridique |
En pratique : Si votre priorité est la sécurité (réagir vite aux CVE), choisissez CycloneDX. Si votre priorité est la conformité légale, choisissez SPDX. Les deux formats coexistent, et les outils modernes supportent généralement les deux.
{ "bomFormat": "CycloneDX", "specVersion": "1.5", "components": [ { "type": "library", "name": "lodash", "version": "4.17.21", "purl": "pkg:npm/lodash@4.17.21", "licenses": [{ "license": { "id": "MIT" }}] } ]}{ "spdxVersion": "SPDX-2.3", "SPDXID": "SPDXRef-DOCUMENT", "packages": [ { "SPDXID": "SPDXRef-Package-lodash", "name": "lodash", "versionInfo": "4.17.21", "licenseConcluded": "MIT" } ]}Génération de SBOM
Section intitulée « Génération de SBOM »Plusieurs outils permettent de générer des SBOM :
# Avec Syft (Anchore) - recommandésyft packages dir:. -o cyclonedx-json > sbom.json
# Avec Trivytrivy fs --format cyclonedx -o sbom.json .
# Pour une image Dockersyft packages registry.example.com/myapp:v1.0.0 -o spdx-json > sbom.jsonIntégration CI/CD
Section intitulée « Intégration CI/CD »name: Generate SBOM
on: push: branches: [main]
jobs: sbom: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Generate SBOM uses: anchore/sbom-action@v0 with: format: cyclonedx-json output-file: sbom.json
- name: Upload SBOM uses: actions/upload-artifact@v4 with: name: sbom path: sbom.jsonSLSA : le framework de maturité
Section intitulée « SLSA : le framework de maturité »SLSA (Supply-chain Levels for Software Artifacts, prononcé “salsa”) est un framework qui définit des niveaux de sécurité progressifs pour la supply chain.
Analogie : Pensez aux normes de sécurité automobile. Une voiture peut avoir :
- Niveau 1 : Des ceintures de sécurité (c’est mieux que rien)
- Niveau 2 : Des airbags (protection automatique)
- Niveau 3 : ABS + contrôle de stabilité (le système vous empêche de faire des erreurs)
- Niveau 4 : Crash-tests 5 étoiles + systèmes redondants (n’importe qui peut vérifier la sécurité)
SLSA fait la même chose pour le logiciel. Au lieu de dire “c’est sécurisé” ou “c’est pas sécurisé”, il définit quatre paliers mesurables qui répondent à une question : Peut-on faire confiance à cet artefact ?
Les quatre niveaux SLSA
Section intitulée « Les quatre niveaux SLSA »Chaque niveau rajoute des garanties techniques qui rendent la falsification plus difficile :
| Niveau | Exigences | Ce que ça prouve | Effort |
|---|---|---|---|
| SLSA 1 | Provenance documentée | Quelqu’un a documenté le build | Faible |
| SLSA 2 | Provenance générée automatiquement | Le système de build a généré la provenance | Moyen |
| SLSA 3 | Build isolé, provenance non falsifiable | Le développeur ne peut pas falsifier | Élevé |
| SLSA 4 | Build hermétique, reproductible | N’importe qui peut vérifier | Très élevé |
Pourquoi une progression ? Parce que passer directement au niveau 4 est irréaliste pour la plupart des organisations. SLSA 2 est un bon compromis : la provenance est générée automatiquement par votre CI/CD (GitHub Actions, GitLab CI), donc un développeur malveillant ne peut pas la modifier facilement. SLSA 3 va plus loin : même avec un accès root au runner CI, la falsification devient extrêmement difficile.
Anatomie de la provenance SLSA
Section intitulée « Anatomie de la provenance SLSA »Une attestation de provenance SLSA contient :
{ "_type": "https://in-toto.io/Statement/v1", "subject": [{ "name": "ghcr.io/myorg/myapp", "digest": { "sha256": "abc123..." } }], "predicateType": "https://slsa.dev/provenance/v1", "predicate": { "buildDefinition": { "buildType": "https://github.com/slsa-framework/slsa-github-generator/container@v1", "externalParameters": { "source": { "uri": "git+https://github.com/myorg/myapp@refs/heads/main", "digest": { "sha1": "def456..." } } } }, "runDetails": { "builder": { "id": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/tags/v1.9.0" }, "metadata": { "invocationId": "https://github.com/myorg/myapp/actions/runs/123456789" } } }}Cette attestation prouve :
- Quoi : L’image
ghcr.io/myorg/myappavec ce digest - D’où : Du repo
myorg/myapp, commitdef456... - Comment : Via le workflow GitHub Actions
generator_container_slsa3.yml - Quand : Run ID
123456789
Implémentation SLSA 3 avec GitHub Actions
Section intitulée « Implémentation SLSA 3 avec GitHub Actions »name: Release with SLSA
on: push: tags: ['v*']
jobs: build: outputs: digest: ${{ steps.build.outputs.digest }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Build and push id: build run: | docker build -t ghcr.io/${{ github.repository }}:${{ github.ref_name }} . docker push ghcr.io/${{ github.repository }}:${{ github.ref_name }} echo "digest=$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/${{ github.repository }}:${{ github.ref_name }} | cut -d'@' -f2)" >> $GITHUB_OUTPUT
provenance: needs: build permissions: actions: read id-token: write packages: write uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0 with: image: ghcr.io/${{ github.repository }} digest: ${{ needs.build.outputs.digest }} registry-username: ${{ github.actor }} secrets: registry-password: ${{ secrets.GITHUB_TOKEN }}Sigstore : l’écosystème de signature
Section intitulée « Sigstore : l’écosystème de signature »Sigstore simplifie radicalement la signature d’artefacts en éliminant le problème le plus complexe : la gestion des clés privées.
Le problème historique : pourquoi personne ne signait
Section intitulée « Le problème historique : pourquoi personne ne signait »Imaginez que pour prouver votre identité, vous deviez porter en permanence un coffre-fort contenant votre passeport. Si vous le perdez, votre identité est volée. Si quelqu’un le vole, il peut se faire passer pour vous. Vous devez le garder accessible 24/7 (pour prouver qui vous êtes), mais absolument sécurisé (pour qu’on ne vous le vole pas). C’est impossible.
Avant Sigstore, signer un artefact logiciel était exactement ce cauchemar :
- Générer une paire de clés : Créer une clé privée (votre passeport numérique)
- Stocker la clé privée de manière sécurisée : Coffre-fort chiffré, HSM, KMS… (coûteux)
- Distribuer la clé publique : Comment les autres savent-ils que cette clé publique est vraiment la vôtre ?
- Gérer la rotation des clés : Changer de clé tous les X mois (cauchemar opérationnel)
- Révoquer les clés compromises : Si la clé fuite, comment annuler toutes les signatures passées ?
Résultat : 95% des projets open source ne signaient rien. C’était trop complexe, trop cher, trop risqué. Les développeurs préféraient ne rien faire plutôt que de mal gérer une clé privée.
Sigstore résout ce problème avec une idée brillante : et si on n’avait pas besoin de gérer des clés privées ?
L’approche keyless de Sigstore
Section intitulée « L’approche keyless de Sigstore »Sigstore utilise des certificats éphémères liés à votre identité OIDC (GitHub, GitLab, Google). Voici comment ça fonctionne concrètement :
Scénario : Vous êtes développeur chez Acme Corp. Votre CI/CD GitHub Actions doit signer une image Docker.
-
Vous vous authentifiez via OIDC → Votre workflow GitHub Actions prouve son identité auprès de Sigstore : “Je suis le workflow
release.ymldu repoacme/app, déclenché par le tagv1.0.0, par l’utilisateuralice.” -
Fulcio émet un certificat de courte durée (10 minutes) → Sigstore vous donne un certificat qui dit : “Ce certificat est valide pendant 10 minutes et atteste que le signataire est bien le workflow GitHub Actions de acme/app.”
-
Vous signez avec ce certificat → Votre image Docker
ghcr.io/acme/app:v1.0.0est signée avec ce certificat temporaire. -
La signature est enregistrée dans Rekor (log de transparence) → Un registre public et immuable enregistre : “Le 20 janvier 2026 à 14h32, le workflow acme/app/.github/workflows/release.yml a signé l’image sha256:abc123…”
-
Le certificat expire, mais la signature reste vérifiable → 10 minutes plus tard, le certificat est inutilisable. Mais n’importe qui peut vérifier la signature en consultant Rekor : “Cette image a bien été signée par le workflow officiel d’Acme Corp.”
Résultat : Pas de clé privée à gérer (pas de risque de fuite), mais une traçabilité complète et vérifiable publiquement. Si un attaquant compromet votre repo, il peut signer une fois pendant 10 minutes, mais chaque signature est enregistrée dans Rekor. L’attaque sera visible.
Les composants Sigstore
Section intitulée « Les composants Sigstore »| Composant | Rôle | Analogie |
|---|---|---|
| Cosign | CLI pour signer/vérifier | Le stylo qui signe |
| Fulcio | Autorité de certification | Le notaire qui vérifie l’identité |
| Rekor | Log de transparence | Le registre public des actes |
| Gitsign | Signature des commits Git | Cosign pour Git |
Signature avec Cosign
Section intitulée « Signature avec Cosign »# Signer (ouvre une fenêtre d'authentification OIDC)cosign sign ghcr.io/myorg/myapp:v1.0.0
# Vérifiercosign verify \ --certificate-identity "https://github.com/myorg/myrepo/.github/workflows/release.yml@refs/tags/v1.0.0" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ ghcr.io/myorg/myapp:v1.0.0# Générer une paire de cléscosign generate-key-pair
# Signercosign sign --key cosign.key ghcr.io/myorg/myapp:v1.0.0
# Vérifiercosign verify --key cosign.pub ghcr.io/myorg/myapp:v1.0.0Vérification dans Kubernetes
Section intitulée « Vérification dans Kubernetes »Kubernetes peut vérifier les signatures avant de déployer une image :
# Policy KyvernoapiVersion: kyverno.io/v1kind: ClusterPolicymetadata: name: verify-signaturesspec: validationFailureAction: Enforce rules: - name: verify-cosign-signature match: resources: kinds: [Pod] verifyImages: - image: "ghcr.io/myorg/*" key: |- -----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----Sécuriser les dépendances
Section intitulée « Sécuriser les dépendances »Les dépendances sont le vecteur d’attaque le plus courant dans la supply chain logicielle. La raison est simple : vous ne contrôlez pas leur code, mais vous l’exécutez avec les mêmes privilèges que le vôtre.
Scénario réel : En 2021, un développeur a compromis son propre package npm ua-parser-js (8 millions de téléchargements par semaine) pour y injecter un cryptominer. En quelques heures, des milliers d’applications ont commencé à miner de la cryptomonnaie pour l’attaquant, sans que personne ne s’en rende compte. Pourquoi ? Parce que les versions n’étaient pas épinglées, et que npm install a automatiquement téléchargé la version compromise.
Plusieurs pratiques complémentaires permettent de réduire ce risque de manière pragmatique.
Épingler les versions : arrêter de faire confiance aux futurs
Section intitulée « Épingler les versions : arrêter de faire confiance aux futurs »Quand vous écrivez "lodash": "^4.17.0" dans votre package.json, vous dites à npm : “Télécharge n’importe quelle version 4.17.x, y compris celles qui n’existent pas encore.” C’est un chèque en blanc.
Si demain le compte npm du mainteneur de lodash est compromis (mot de passe faible, pas de 2FA), l’attaquant peut publier lodash@4.17.22 avec un backdoor. Votre prochain npm install téléchargera cette version malveillante. Vous aurez fait confiance à une version que vous n’avez jamais testée.
Ne laissez jamais le gestionnaire de packages choisir la version pour vous :
// ❌ Dangereux{ "dependencies": { "lodash": "^4.17.0", // Accepte 4.17.x, y compris futures versions compromises "express": "*" // N'importe quelle version ! }}
// ✅ Sécurisé{ "dependencies": { "lodash": "4.17.21", // Version exacte "express": "4.18.2" }}Utiliser des lock files : figer l’arbre complet
Section intitulée « Utiliser des lock files : figer l’arbre complet »Épingler les versions dans package.json ne suffit pas. Pourquoi ? Parce que vos dépendances ont elles-mêmes des dépendances (dépendances transitives), et vous ne contrôlez pas leurs versions.
Exemple : Vous épinglez express@4.18.2. Mais Express dépend de body-parser, qui dépend de iconv-lite, qui dépend de safer-buffer. Si demain safer-buffer publie une nouvelle version avec un malware, et que iconv-lite accepte cette version, votre build téléchargera le malware même si vous n’avez jamais entendu parler de safer-buffer.
Les lock files (package-lock.json, yarn.lock, Pipfile.lock) résolvent ce problème en figeant l’arbre complet des dépendances : chaque package, chaque version, chaque hash. C’est une photo complète de l’état du monde au moment du lock.
# Installer uniquement ce qui est dans le lock filenpm ci # Pas `npm install`Pourquoi npm ci et pas npm install ? Parce que npm install peut mettre à jour le lock file si de nouvelles versions compatibles sont disponibles. npm ci refuse : il installe exactement ce qui est dans le lock file, ou il échoue. C’est ce que vous voulez en CI/CD.
Vérifier les checksums : détecter les substitutions
Section intitulée « Vérifier les checksums : détecter les substitutions »Le problème : Même avec un lock file, un attaquant qui contrôle le registry (ou un man-in-the-middle) peut substituer un package par une version malveillante qui porte le même nom et la même version.
La solution : Les lock files modernes incluent des hashes cryptographiques (SHA-512) de chaque package. Si le contenu téléchargé ne correspond pas au hash attendu, l’installation échoue.
# npm vérifie automatiquement les checksums du lock filenpm ci --ignore-scripts # Bonus : désactive les scripts post-install
# Python avec pip-toolspip-compile requirements.in --generate-hashespip-sync requirements.txtPourquoi --ignore-scripts ? Parce que certains packages npm exécutent des scripts arbitraires après installation (post-install hooks). Si un package est compromis, ce script peut exfiltrer vos secrets. En désactivant les scripts, vous limitez la surface d’attaque.
Scanner les vulnérabilités : réagir aux CVE connues
Section intitulée « Scanner les vulnérabilités : réagir aux CVE connues »Le contexte : Chaque semaine, de nouvelles vulnérabilités (CVE) sont publiées. Si vous ne scannez pas vos dépendances, vous ne saurez pas que vous êtes vulnérable. C’est comme conduire une voiture rappelée pour défaut de frein sans le savoir.
Les scanners de vulnérabilités comparent vos dépendances à des bases de données de CVE connues (NVD, GitHub Advisory Database, etc.) et vous alertent instantanément.
# npm audit (intégré à npm)npm audit --audit-level=high
# Trivy (multi-écosystème : npm, pip, Go, Rust, Java...)trivy fs --severity HIGH,CRITICAL .
# Grype (Anchore, alternative à Trivy)grype dir:.En pratique : Intégrez un de ces scanners dans votre CI/CD. Si une vulnérabilité HIGH ou CRITICAL est détectée, le build échoue. Vous forcez ainsi les équipes à corriger avant de déployer.
Roadmap d’implémentation
Section intitulée « Roadmap d’implémentation »La sécurisation de la supply chain est un voyage, pas une destination. Plutôt que de tout implémenter d’un coup, adoptez une approche progressive qui construit la maturité par étapes :
-
Mois 1-2 : Visibilité
- Générer des SBOM pour les projets critiques
- Scanner les dépendances (SCA) dans la CI
- Inventorier les registries et sources de dépendances
-
Mois 3-4 : Intégrité
- Signer les images Docker avec Cosign
- Vérifier les checksums des dépendances
- Activer les lock files et
npm ci
-
Mois 5-6 : Provenance
- Générer des attestations SLSA (niveau 2)
- Configurer la vérification de signatures dans Kubernetes
- Auditer les workflows CI/CD
-
Mois 7+ : Maturité
- Viser SLSA niveau 3
- Déployer Dependency-Track pour le suivi centralisé
- Automatiser la génération de VEX
À retenir
Section intitulée « À retenir »- SBOM : Savoir ce qui compose vos logiciels (obligatoire pour répondre aux CVE)
- SLSA : Framework de maturité, viser niveau 2 minimum
- Sigstore : Signature keyless, plus d’excuse pour ne pas signer
- Dépendances : Épingler, verrouiller, scanner, vérifier
- Progressivité : Commencer par la visibilité, puis intégrité, puis provenance