Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

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 :

bash
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

CasCe qui s'est passéLe maillon faible
Equifax, 2017Une faille d'Apache Struts exploitée deux mois après la publication du correctifUne dépendance connue vulnérable, pas mise à jour
event-stream, 2018Un nouveau mainteneur, à qui l'ancien avait confié le paquet npm, y ajoute une dépendance malveillante qui vise un portefeuille de cryptomonnaieLa confiance accordée à un mainteneur inconnu
SolarWinds, 2020Le système de build de l'éditeur est compromis ; une mise à jour piégée est installée par environ 18 000 organisationsL'outil de build lui-même
Log4Shell, 2021Une 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, 2024Aprè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 diffusionUn projet libre essentiel, maintenu par une seule personne
Shai-Hulud, 2025Premier 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 novembreLes 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 CVSSGravité
0,0Aucune
0,1 à 3,9Faible
4,0 à 6,9Moyenne
7,0 à 8,9Élevée
9,0 à 10,0Critique

Le score se calcule à partir d'un vecteur. Celui de Log4Shell, en CVSS 3.1 :

text
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H   →   10,0 (critique)
MétriqueValeurSens
AV:NVecteur d'attaque : réseauExploitable à distance, par Internet
AC:LComplexité : faibleAucune condition particulière
PR:NPrivilèges requis : aucunPas besoin de compte
UI:NInteraction utilisateur : aucunePersonne n'a besoin de cliquer
S:CPortée : changéeL'attaque déborde du composant vulnérable
C:H/I:H/A:HImpact é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 :

text
$ 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.

CommandeEffet
npm auditListe les vulnérabilités connues
npm audit --omit=devIgnore les dépendances de développement, absentes de la production
npm audit --audit-level=highCode de sortie en erreur seulement à partir de « élevée » : pratique en intégration continue
npm audit fixMet à jour vers des versions corrigées compatibles avec les plages du package.json
npm outdatedListe les dépendances qui ont des versions plus récentes
npm ciInstalle 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.jsonVersions acceptéesExemple pour 1.4.2
1.4.2Exactement celle-ci1.4.2
~1.4.2Correctifs seulementde 1.4.2 à moins de 1.5.0
^1.4.2 (défaut de npm install)Correctifs et mineuresde 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 :

yaml
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 :

bash
npm sbom --sbom-format cyclonedx > sbom.json

Un extrait du fichier produit :

json
{
  "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 postinstall s'exécute sur ta machine dès le npm install : c'est par là qu'est passé Shai-Hulud. L'option npm install --ignore-scripts, ou ignore-scripts=true dans 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 :

html
<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 :

text
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
  1. Quel critère DICP cette faille menace-t-elle ?
  2. La correction casse-t-elle la compatibilité ? Comment le sais-tu ?
  3. Que fais-tu, dans l'ordre ?
  4. 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
  1. La disponibilité : un déni de service rend l'API indisponible.
  2. Non : le rapport propose npm audit fix sans --force et n'annonce pas de breaking change. La version corrigée respecte la plage déclarée dans package.json : la plage ^2.0.0 accepte 2.0.2.
  3. Lancer npm audit fix, relancer les tests, vérifier l'envoi de justificatifs (la fonction qui utilise multer), commiter le package-lock.json mis à jour, puis déployer.
  4. 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.

  1. Quelles versions npm install peut-il installer pour chacune, sans lock-file ?
  2. Pourquoi versionner le package-lock.json ?
  3. Quelle commande utiliser sur le serveur de production ?
Voir la réponseMasquer la réponse de l'exercice 2
  1. ^5.0.1 accepte toute version 5.x à partir de 5.0.1 : express 5.1.0 (pas 6.0.0, version majeure). ~8.0.0 accepte seulement les correctifs 8.0.x : helmet 8.0.3 (pas 8.1.0).
  2. 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.
  3. npm ci (avec --omit=dev si 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.

  1. Comment répondre en dix minutes si l'équipe tient des SBOM ?
  2. Et sans SBOM ?
  3. Pourquoi faut-il inclure les dépendances transitives dans le SBOM ?
Voir la réponseMasquer la réponse de l'exercice 3
  1. 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.
  2. 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.
  3. 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.

  1. Une page charge https://cdn.example.com/lib.js sans attribut integrity.
  2. Le projet dépend d'une bibliothèque abandonnée depuis quatre ans, avec deux CVE non corrigées.
  3. L'API reconstruit un objet PHP avec unserialize($_COOKIE['panier']).
  4. Un développeur installe lodahs au lieu de lodash.
Voir la réponseMasquer la réponse de l'exercice 4
  1. A08 : du code est exécuté sans vérification d'intégrité.
  2. A03 : composant vulnérable et non maintenu.
  3. A08 : désérialisation de données non fiables. Un attaquant qui forge le cookie peut provoquer l'exécution de code.
  4. A03 : typosquatting, un paquet malveillant entre dans la chaîne d'approvisionnement.

Tu avais trouvé ?

Chapitre terminé !

Fais les 4 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Défaillances cryptographiques (A04) : hacher, chiffrer, HTTPSRevoir le sommaire du cours