Aller au contenu
Culture DevOps high
🚧 Section en cours de réécriture — Le contenu est en cours de restructuration et peut évoluer.

Sécurité de la supply chain logicielle

14 min de lecture

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é.

La supply chain logicielle englobe tout ce qui contribue à la création et à la distribution d’un logiciel :

ComposantExemplesRisques associés
Code sourceVotre code, contributions externesInjection de code malveillant
Dépendancesnpm, PyPI, Maven, Go modulesCVE, typosquatting, compte compromis
Outils de buildCompilateurs, bundlers, CI/CDCompromission du processus de build
ArtefactsImages Docker, binaires, packagesTampering, substitution
InfrastructureRegistries, CDN, serveurs de déploiementMan-in-the-middle, compromission

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

La sécurisation de la supply chain repose sur trois piliers complémentaires :

PilierQuestionSolution
TransparenceQu’est-ce qui compose mon logiciel ?SBOM
IntégritéMon logiciel a-t-il été modifié ?Signatures, SLSA
ProvenanceD’où vient mon logiciel ?Attestations, logs de transparence

Ces trois piliers se renforcent mutuellement. Un SBOM sans vérification d’intégrité peut être falsifié. Une signature sans SBOM ne dit rien du contenu. Une attestation sans les deux manque de contexte.

Un SBOM (Software Bill of Materials) est la liste exhaustive de tous les composants d’un logiciel : dépendances directes, transitives, licences, versions.

Sans SBOM, vous êtes aveugle. Quand une vulnérabilité comme Log4Shell est annoncée, vous devez pouvoir répondre en minutes, pas en semaines :

QuestionSans SBOMAvec 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 projetsExport filtré instantané
Quelles licences sont présentes ?Analyse juridique coûteuseListe générée automatiquement

Deux formats dominent l’écosystème :

FormatOrigineForcesCas d’usage
CycloneDXOWASPRiche en métadonnées de sécurité, VEX intégréSécurité, vulnérabilités
SPDXLinux FoundationStandard ISO, focus licencesConformité, juridique
{
"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" }}]
}
]
}

Plusieurs outils permettent de générer des SBOM :

Fenêtre de terminal
# Avec Syft (Anchore) - recommandé
syft packages dir:. -o cyclonedx-json > sbom.json
# Avec Trivy
trivy fs --format cyclonedx -o sbom.json .
# Pour une image Docker
syft packages registry.example.com/myapp:v1.0.0 -o spdx-json > sbom.json
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.json

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.

NiveauExigencesCe que ça prouveEffort
SLSA 1Provenance documentéeQuelqu’un a documenté le buildFaible
SLSA 2Provenance générée automatiquementLe système de build a généré la provenanceMoyen
SLSA 3Build isolé, provenance non falsifiableLe développeur ne peut pas falsifierÉlevé
SLSA 4Build hermétique, reproductibleN’importe qui peut vérifierTrès élevé

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/myapp avec ce digest
  • D’où : Du repo myorg/myapp, commit def456...
  • Comment : Via le workflow GitHub Actions generator_container_slsa3.yml
  • Quand : Run ID 123456789
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 simplifie radicalement la signature d’artefacts en éliminant le problème le plus complexe : la gestion des clés privées.

Avant Sigstore, signer un artefact nécessitait :

  1. Générer une paire de clés
  2. Stocker la clé privée de manière sécurisée
  3. Distribuer la clé publique
  4. Gérer la rotation des clés
  5. Révoquer les clés compromises

Cette complexité explique pourquoi peu de projets signaient leurs artefacts.

Sigstore utilise des certificats éphémères liés à votre identité OIDC (GitHub, GitLab, Google) :

  1. Vous vous authentifiez via OIDC
  2. Fulcio émet un certificat de courte durée (10 minutes)
  3. Vous signez avec ce certificat
  4. La signature est enregistrée dans Rekor (log de transparence)
  5. Le certificat expire, mais la signature reste vérifiable

Résultat : Pas de clé privée à gérer, mais une traçabilité complète.

ComposantRôleAnalogie
CosignCLI pour signer/vérifierLe stylo qui signe
FulcioAutorité de certificationLe notaire qui vérifie l’identité
RekorLog de transparenceLe registre public des actes
GitsignSignature des commits GitCosign pour Git
Fenêtre de terminal
# Signer (ouvre une fenêtre d'authentification OIDC)
cosign sign ghcr.io/myorg/myapp:v1.0.0
# Vérifier
cosign 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

Kubernetes peut vérifier les signatures avant de déployer une image :

# Policy Kyverno
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-signatures
spec:
validationFailureAction: Enforce
rules:
- name: verify-cosign-signature
match:
resources:
kinds: [Pod]
verifyImages:
- image: "ghcr.io/myorg/*"
key: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----

Les dépendances sont le vecteur d’attaque le plus courant dans la supply chain logicielle. Plusieurs pratiques complémentaires réduisent significativement ce risque.

Ne laissez jamais le gestionnaire de packages choisir la version :

// ❌ 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"
}
}

Les lock files (package-lock.json, yarn.lock, Pipfile.lock) garantissent des builds reproductibles en figeant l’arbre complet des dépendances :

Fenêtre de terminal
# Installer uniquement ce qui est dans le lock file
npm ci # Pas `npm install`
Fenêtre de terminal
# npm vérifie automatiquement les checksums du lock file
npm ci --ignore-scripts # Bonus : désactive les scripts post-install
# Python avec pip-tools
pip-compile requirements.in --generate-hashes
pip-sync requirements.txt
Fenêtre de terminal
# npm audit
npm audit --audit-level=high
# Trivy (multi-écosystème)
trivy fs --severity HIGH,CRITICAL .
# Grype (Anchore)
grype dir:.

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 :

  1. 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
  2. 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
  3. 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
  4. Mois 7+ : Maturité

    • Viser SLSA niveau 3
    • Déployer Dependency-Track pour le suivi centralisé
    • Automatiser la génération de VEX
  • 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