Chapitre 516 minIntermédiaire
Chaîne d'approvisionnement (A03) et intégrité (A08) : dépendances et SBOM
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- lire une CVE et son score CVSS ;
- auditer et mettre à jour les dépendances d'un projet ;
- produire le SBOM d'une application et dire à quoi il sert ;
- vérifier l'intégrité d'un script ou d'un paquet.
4 exercices corrigés
Une défaillance de la chaîne d'approvisionnement logicielle (Software Supply Chain Failures, A03:2025) survient quand un composant que ton application n'a pas écrit elle-même la rend vulnérable : une dépendance qui contient une faille connue, un paquet piégé, un outil de build ou un pipeline compromis. Ce chapitre explique comment lire une CVE, auditer et mettre à jour les dépendances (npm audit, Dependabot), produire un SBOM, puis comment vérifier l'intégrité du code et des données que tu charges (A08:2025).
Ton application est surtout faite du code des autres
Un projet Express ou React déclare une vingtaine de dépendances dans son package.json. Chacune a les siennes, qui ont les leurs : ce sont les dépendances transitives. Une fois installé, un projet modeste contient couramment plusieurs centaines de paquets dans node_modules. Compte-les sur ton propre projet :
npm ls --all | wc -l # nombre approximatif de paquets installés, directs et transitifs
npm ls --depth=0 # seulement les dépendances directes
Tu n'as relu le code d'aucune d'elles, et chacune s'exécute avec les mêmes droits que ton application. Une faille dans une bibliothèque de troisième niveau, dont tu ignores jusqu'au nom, est une faille de ton application.
Les attaques sur la chaîne d'approvisionnement
| Cas | Ce qui s'est passé | Le maillon faible |
|---|---|---|
| Equifax, 2017 | Une faille d'Apache Struts exploitée deux mois après la publication du correctif | Une dépendance connue vulnérable, pas mise à jour |
| event-stream, 2018 | Un nouveau mainteneur, à qui l'ancien avait confié le paquet npm, y ajoute une dépendance malveillante qui vise un portefeuille de cryptomonnaie | La confiance accordée à un mainteneur inconnu |
| SolarWinds, 2020 | Le système de build de l'éditeur est compromis ; une mise à jour piégée est installée par environ 18 000 organisations | L'outil de build lui-même |
| Log4Shell, 2021 | Une fonctionnalité de Log4j permet d'exécuter du code à distance via un simple message journalisé | Une bibliothèque transitive présente partout, souvent sans qu'on le sache |
| xz-utils, 2024 | Après des années de contributions pour gagner la confiance du projet, un contributeur glisse une porte dérobée dans un outil de compression de Linux (CVE-2024-3094), découverte juste avant sa diffusion | Un projet libre essentiel, maintenu par une seule personne |
| Shai-Hulud, 2025 | Premier ver npm qui se propage seul : des versions piégées volent les jetons de publication des développeurs et publient d'autres versions piégées ; plus de 500 paquets touchés en septembre 2025, puis une deuxième vague en novembre | Les scripts d'installation et les jetons npm des développeurs |
À ces cas s'ajoute le typosquatting : un paquet malveillant publié sous un nom proche d'un paquet populaire (expresss, reqeusts), qui attend une faute de frappe dans un npm install.
CVE et CVSS, lire un avis de sécurité
Une vulnérabilité publiée reçoit un identifiant CVE et une fiche dans les bases publiques : la NVD américaine, la base d'avis de GitHub (identifiants GHSA-…), la base OSV. La fiche indique les versions touchées, les versions corrigées, la nature de la faiblesse (CWE) et un score CVSS.
| Score CVSS | Gravité |
|---|---|
| 0,0 | Aucune |
| 0,1 à 3,9 | Faible |
| 4,0 à 6,9 | Moyenne |
| 7,0 à 8,9 | Élevée |
| 9,0 à 10,0 | Critique |
Le score se calcule à partir d'un vecteur. Celui de Log4Shell, en CVSS 3.1 :
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H → 10,0 (critique)
| Métrique | Valeur | Sens |
|---|---|---|
AV:N | Vecteur d'attaque : réseau | Exploitable à distance, par Internet |
AC:L | Complexité : faible | Aucune condition particulière |
PR:N | Privilèges requis : aucun | Pas besoin de compte |
UI:N | Interaction utilisateur : aucune | Personne n'a besoin de cliquer |
S:C | Portée : changée | L'attaque déborde du composant vulnérable |
C:H/I:H/A:H | Impact élevé sur la confidentialité, l'intégrité, la disponibilité | Prise de contrôle complète |
La version 4.0 de CVSS, publiée fin 2023, ajoute des métriques mais garde la même échelle ; beaucoup de bases affichent encore les deux. Le score mesure la gravité technique : une faille critique dans une fonction que ton application n'appelle jamais pèse moins qu'une faille moyenne sur ta page de connexion. Lis toujours la fiche avant de décider.
Dans un rapport d'audit, on distingue donc le score technique (le CVSS de base, qui ne change pas) et la gravité retenue, que l'auditeur peut relever ou abaisser selon le contexte : données exposées, obligations légales, nombre de personnes touchées. Il l'écrit et la justifie. La fiche de vulnérabilité du chapitre 14 en donne un exemple : un score de 6,5 (« Moyenne ») relevé à « Élevée » parce que des RIB fuient.
npm audit pas à pas
npm audit compare les versions installées (d'après package-lock.json) à la base d'avis de sécurité du registre npm. Un extrait typique :
$ npm audit
# npm audit report
jsonwebtoken <=8.5.1
Severity: high
jsonwebtoken unrestricted key type could lead to legacy keys usage - https://github.com/advisories/GHSA-8cf7-32gw-wr33
fix available via `npm audit fix --force`
Will install jsonwebtoken@9.0.2, which is a breaking change
node_modules/jsonwebtoken
1 high severity vulnerability
Comment le lire : le paquet et la plage de versions touchées, la gravité, le titre et le lien de l'avis, la correction disponible. Ici, la correction impose de passer à une version majeure supérieure (breaking change) : npm audit fix seul ne le fera pas, et --force peut casser ton code. On met donc à jour à la main, on lit les notes de version, on relance les tests.
| Commande | Effet |
|---|---|
npm audit | Liste les vulnérabilités connues |
npm audit --omit=dev | Ignore les dépendances de développement, absentes de la production |
npm audit --audit-level=high | Code de sortie en erreur seulement à partir de « élevée » : pratique en intégration continue |
npm audit fix | Met à jour vers des versions corrigées compatibles avec les plages du package.json |
npm outdated | Liste les dépendances qui ont des versions plus récentes |
npm ci | Installe exactement les versions du package-lock.json ; échoue s'il ne correspond pas au package.json |
Les plages de versions suivent le versionnage sémantique (semver) MAJEUR.MINEUR.CORRECTIF : un correctif ne change rien à l'utilisation, une version mineure ajoute des fonctions sans rien casser, une majeure peut casser.
Écriture dans package.json | Versions acceptées | Exemple pour 1.4.2 |
|---|---|---|
1.4.2 | Exactement celle-ci | 1.4.2 |
~1.4.2 | Correctifs seulement | de 1.4.2 à moins de 1.5.0 |
^1.4.2 (défaut de npm install) | Correctifs et mineures | de 1.4.2 à moins de 2.0.0 |
Le fichier package-lock.json fige les versions réellement installées, dépendances transitives comprises, avec une empreinte de chaque paquet. Versionne-le, et utilise npm ci sur les serveurs et en intégration continue : tout le monde installe alors exactement les mêmes paquets.
Les autres écosystèmes ont leur équivalent : composer audit en PHP, pip-audit en Python, dotnet list package --vulnerable en .NET, OWASP Dependency-Check pour Java.
Automatiser la surveillance : Dependabot
Une dépendance saine aujourd'hui peut recevoir une CVE critique demain. La surveillance doit donc être continue. Sur GitHub, Dependabot envoie des alertes quand une dépendance du dépôt est touchée et ouvre des pull requests de mise à jour. Le fichier .github/dependabot.yml règle les mises à jour de versions :
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
Chaque pull request déclenche les tests ; tu fusionnes quand ils passent. Renovate, sur GitHub ou GitLab, rend le même service. Le cours Git et GitHub explique les pull requests.
Le SBOM, l'inventaire de ton logiciel
Le SBOM (Software Bill of Materials, nomenclature logicielle) est la liste complète et structurée des composants d'un logiciel : chaque paquet, sa version, sa licence, son origine, directes et transitives comprises. C'est l'équivalent de la liste des ingrédients sur un emballage. Le jour où une faille comme Log4Shell est publiée, une organisation qui tient les SBOM de ses applications sait en quelques minutes lesquelles sont touchées ; les autres fouillent leurs serveurs pendant des jours.
Deux formats standard dominent : CycloneDX (porté par l'OWASP) et SPDX (porté par la Linux Foundation, norme ISO/IEC 5962). npm en génère un directement :
npm sbom --sbom-format cyclonedx > sbom.json
Un extrait du fichier produit :
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"components": [
{
"type": "library",
"name": "express",
"version": "5.1.0",
"purl": "pkg:npm/express@5.1.0",
"licenses": [{ "license": { "id": "MIT" } }]
}
]
}
Des outils comme OWASP Dependency-Track importent ces SBOM et surveillent en continu chaque composant face aux nouvelles CVE. En Europe, le Cyber Resilience Act (règlement (UE) 2024/2847) impose aux fabricants de produits comportant des éléments numériques de documenter leurs composants, notamment par un SBOM ; ses obligations de signalement des vulnérabilités s'appliquent à partir de septembre 2026 et l'essentiel du texte à partir de décembre 2027.
Choisir et installer une dépendance avec prudence
Avant d'ajouter un paquet, quelques vérifications :
- En as-tu vraiment besoin ? Trois lignes de code valent mieux qu'une dépendance de plus.
- Est-il maintenu ? Date de la dernière version, issues ouvertes sans réponse, nombre de mainteneurs.
- Est-ce le bon nom ? Vérifie l'orthographe et le lien vers le dépôt officiel sur la page npm.
- Quelle licence ? Compatible avec ton projet (cours Droit informatique).
- Exécute-t-il des scripts à l'installation ? Un script
postinstalls'exécute sur ta machine dès lenpm install: c'est par là qu'est passé Shai-Hulud. L'optionnpm install --ignore-scripts, ouignore-scripts=truedans le fichier.npmrc, les désactive ; quelques paquets légitimes en ont besoin, à autoriser au cas par cas.
Côté comptes, active la double authentification sur npm et GitHub, et utilise des jetons de publication à durée limitée et aux droits réduits : un jeton volé ne doit pas permettre de publier tous tes paquets.
A08, vérifier l'intégrité du code et des données
Les défaillances d'intégrité des logiciels ou des données (Software or Data Integrity Failures, A08:2025) concernent du code ou des données que l'application utilise sans vérifier qu'ils n'ont pas été modifiés ou qu'ils viennent bien de la source attendue. A03 parle de composants vulnérables ou piégés ; A08 parle de l'absence de vérification.
Un script chargé depuis un CDN. Si le CDN est compromis, ton site exécute le script modifié. L'attribut integrity (Subresource Integrity, SRI) donne l'empreinte attendue du fichier ; le navigateur refuse de l'exécuter si elle ne correspond pas :
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.6/dist/purify.min.js"
integrity="sha384-…empreinte fournie par le CDN…"
crossorigin="anonymous"></script>
L'empreinte se calcule à partir du fichier exact : openssl dgst -sha384 -binary purify.min.js | openssl base64 -A. Toute modification, même d'un octet, la change. Une version précise est indispensable : une URL du type @latest change de contenu, donc d'empreinte.
Des paquets signés. Le registre npm signe les paquets et peut attester de leur provenance (le dépôt et le pipeline qui les ont construits) ; npm audit signatures vérifie ces signatures pour les paquets installés.
Des mises à jour et des pipelines protégés. Une application qui télécharge et installe ses mises à jour sans vérifier leur signature installera aussi une mise à jour piégée. Le pipeline d'intégration continue, qui construit et déploie, doit être protégé comme la production : branches protégées, relecture obligatoire, secrets limités, actions tierces épinglées à une version précise.
La désérialisation de données non fiables. Sérialiser, c'est transformer un objet en texte ou en octets pour le stocker ou le transmettre ; désérialiser, c'est le reconstruire. Certains formats reconstruisent des objets avec leur comportement : unserialize() en PHP, ObjectInputStream en Java, la bibliothèque node-serialize en Node.js. Désérialiser une donnée fournie par l'utilisateur peut alors exécuter du code. La parade : n'échanger que des données simples en JSON (json_decode, JSON.parse, qui ne créent que des tableaux, des objets simples et des valeurs), et valider leur structure.
Des données de confiance non signées. Un cookie role=visiteur que le serveur relit tel quel se modifie en role=admin. Toute donnée que le client conserve et renvoie doit être signée par le serveur (c'est ce que fait un jeton JWT bien vérifié, chapitre 11) ou, mieux, rester côté serveur.
Exercice 1 sur 4 Lire un rapport npm audit Réussi À revoir
Le package.json d'AppliFrais déclare "multer": "^2.0.0". Un npm audit affiche ceci :
multer 1.4.4-lts.1 - 2.0.1
Severity: high
Multer vulnerable to Denial of Service via unhandled exception from malformed request - https://github.com/advisories/GHSA-fjgf-rc76-4x9p
fix available via `npm audit fix`
node_modules/multer
- Quel critère DICP cette faille menace-t-elle ?
- La correction casse-t-elle la compatibilité ? Comment le sais-tu ?
- Que fais-tu, dans l'ordre ?
- Dans quelle catégorie du Top 10 2025 ranges-tu « utiliser une version vulnérable connue » ?
Voir la réponseMasquer la réponse de l'exercice 1
- La disponibilité : un déni de service rend l'API indisponible.
- Non : le rapport propose
npm audit fixsans--forceet n'annonce pas de breaking change. La version corrigée respecte la plage déclarée danspackage.json: la plage^2.0.0accepte 2.0.2. - Lancer
npm audit fix, relancer les tests, vérifier l'envoi de justificatifs (la fonction qui utilisemulter), commiter lepackage-lock.jsonmis à jour, puis déployer. - A03:2025 Défaillances de la chaîne d'approvisionnement logicielle.
Tu avais trouvé ?
Exercice 2 sur 4 Versions et plages Réussi À revoir
Le package.json déclare "express": "^5.0.1" et "helmet": "~8.0.0". Suppose que les versions publiées les plus récentes soient express 5.1.0 et 6.0.0, helmet 8.0.3 et 8.1.0.
- Quelles versions
npm installpeut-il installer pour chacune, sans lock-file ? - Pourquoi versionner le
package-lock.json? - Quelle commande utiliser sur le serveur de production ?
Voir la réponseMasquer la réponse de l'exercice 2
^5.0.1accepte toute version 5.x à partir de 5.0.1 : express 5.1.0 (pas 6.0.0, version majeure).~8.0.0accepte seulement les correctifs 8.0.x : helmet 8.0.3 (pas 8.1.0).- Pour que toute l'équipe, l'intégration continue et la production installent exactement les mêmes versions, dépendances transitives comprises, avec leurs empreintes vérifiées. Sans lui, deux installations à une semaine d'écart peuvent différer.
npm ci(avec--omit=devsi les dépendances de développement ne servent pas en production).
Tu avais trouvé ?
Exercice 3 sur 4 SBOM et crise Réussi À revoir
Un lundi matin, une faille critique est annoncée dans une bibliothèque de traitement d'images très répandue. Ton responsable te demande lesquelles des douze applications du service sont touchées.
- Comment répondre en dix minutes si l'équipe tient des SBOM ?
- Et sans SBOM ?
- Pourquoi faut-il inclure les dépendances transitives dans le SBOM ?
Voir la réponseMasquer la réponse de l'exercice 3
- On cherche le nom du paquet (et ses versions touchées) dans les SBOM des douze applications, à la main ou avec un outil comme Dependency-Track qui fait la recherche automatiquement et signale déjà les composants touchés.
- Il faut récupérer le code de chaque application, installer les dépendances ou lire les fichiers de verrouillage un par un, et vérifier aussi les serveurs où elles tournent : des heures ou des jours, avec un risque d'oubli.
- Parce que la bibliothèque vulnérable est rarement une dépendance directe : elle est souvent embarquée par une autre bibliothèque. Log4Shell touchait surtout des applications qui ne savaient pas qu'elles utilisaient Log4j.
Tu avais trouvé ?
Exercice 4 sur 4 Intégrité ou chaîne d'approvisionnement ? Réussi À revoir
Classe chaque situation en A03:2025 ou A08:2025.
- Une page charge
https://cdn.example.com/lib.jssans attributintegrity. - Le projet dépend d'une bibliothèque abandonnée depuis quatre ans, avec deux CVE non corrigées.
- L'API reconstruit un objet PHP avec
unserialize($_COOKIE['panier']). - Un développeur installe
lodahsau lieu delodash.
Voir la réponseMasquer la réponse de l'exercice 4
- A08 : du code est exécuté sans vérification d'intégrité.
- A03 : composant vulnérable et non maintenu.
- A08 : désérialisation de données non fiables. Un attaquant qui forge le cookie peut provoquer l'exécution de code.
- A03 : typosquatting, un paquet malveillant entre dans la chaîne d'approvisionnement.
Tu avais trouvé ?