Leçon 13/14 10 min

Dépendances et supply chain

Tu écris 200 lignes, tu en charges un million. Une faille dans une dép est ta faille. Audit, lockfile, zéro confiance.

Tu écris 200 lignes, tu en installes un million

Depuis le début du cours, on a sécurisé votre code : vos requêtes SQL, vos tokens, vos sessions, vos clés cryptographiques. Cette leçon élargit le périmètre : et si la menace venait du code des autres, celui dont vous dépendez sans l'avoir écrit ?

Vous démarrez un projet, vous écrivez deux cents lignes. Puis vous tapez npm install, et votre dossier node_modules se remplit de plus de mille paquets écrits par des inconnus. Dans une app moderne, 90 % de ce qui tourne, vous ne l'avez ni écrit ni lu.

Vous écrivez peu, vous installez énormément de code d'inconnus Votre code : ~200 lignes que vous avez écrites + Vos dépendances directes : ~20 paquets choisis + + leurs dépendances (transitives) : souvent 1000+
90 % de ce qui tourne dans votre app, vous ne l'avez pas écrit. Une faille dans n'importe lequel devient la vôtre.

Le jour où l'un de ces paquets a une faille, ou devient malveillant, c'est votre app qui tombe. L'exemple resté célèbre : Log4Shell (2021). Une faille dans une banale bibliothèque de logs Java a permis d'exécuter du code à distance sur des millions de serveurs. Beaucoup de ces apps n'utilisaient même pas cette lib directement : elle était cachée dans leurs dépendances.

La règle est dure mais simple : une faille dans une dépendance est votre faille. Vos utilisateurs se moquent que le bug vienne d'une bibliothèque ; c'est votre site qui fuit. C'est l'A03 de l'OWASP 2025 (Software Supply Chain Failures), une catégorie devenue assez grave pour avoir sa place dans le top 3.

Les deux dangers

1. La dépendance vulnérable (le plus courant). Une bibliothèque que vous utilisez a une faille publiée (un CVE). Si vous ne la mettez pas à jour, vous restez exposé. C'est passif : la faille est connue, documentée, et l'attaquant la cherche chez vous. C'est le chemin le plus rentable pour lui : pas besoin de trouver une faille inédite, juste une lib pas à jour.

2. L'attaque de la supply chain (plus sournois). Là, l'attaquant compromet la dépendance elle-même :

  • Paquet malveillant : une version piégée est publiée, parce que le compte du mainteneur a été piraté, ou que le mainteneur est mal intentionné.
  • Typosquatting : un paquet nommé lodahs au lieu de lodash. Une faute de frappe, et vous installez le malware.
  • Dependency confusion : vous avez un paquet privé interne. L'attaquant publie un paquet public du même nom, avec un numéro de version plus élevé. Le gestionnaire choisit le sien, croyant qu'il est plus récent.

Le point commun de tout ça : npm install (ou composer install) exécute du code d'inconnus sur votre machine et en production, avec vos droits. Les scripts d'installation (postinstall) tournent tout seuls, sans rien demander.

Le faux sentiment : « j'ai installé une lib super populaire, donc c'est sûr ». La popularité aide, mais ne garantit rien : des paquets très utilisés ont été compromis (compte mainteneur piraté). Et vous héritez aussi des dépendances de vos dépendances (transitives), que vous n'avez jamais choisies. La confiance se vérifie, elle ne se présume pas (section 4).

À vous d'attaquer : installez le paquet piégé

Voici deux projets. L'un est négligé (dépendances non vérifiées), l'autre est sous contrôle (versions épinglées, audit, noms vérifiés). Lancez npm install sur chacun, et un npm audit, pour voir la différence. Tout est simulé.

Ton projet · dépendances

        

    
Ce qui vient de se passer

Projet négligé : npm install a installé lodahs, un typosquat de lodash. Son script postinstall s'est exécuté tout seul et a exfiltré vos secrets. Une faute de frappe a suffi à faire tourner du code malveillant, avec vos droits. Et lodash 4.17.4 traîne une faille connue (CVE).

Projet sous contrôle : l'install se fait depuis le lockfile (versions épinglées + hash d'intégrité), npm audit est vert, et le nom des paquets a été vérifié. Remarquez : l'audit signale bien la version vulnérable, mais il ne détecte pas le typosquat. Ça, c'est à l'œil humain.

Le correctif : l'hygiène des dépendances

Pas de formule magique, mais quelques réflexes qui changent tout.

Auditer, en continu. Les gestionnaires savent croiser vos dépendances avec les failles connues :

npm audit              # liste les CVE de tes dépendances (JS)
npm audit fix          # met à jour ce qui peut l'être sans casser
composer audit         # idem côté PHP

On automatise tout ça dans la CI, et on laisse un robot (Dependabot, Renovate) ouvrir les pull requests de mise à jour.

Épingler et vérifier l'intégrité. Le lockfile (package-lock.json, composer.lock) fige les versions exactes et un hash pour chaque paquet. À chaque install, vous obtenez les mêmes octets. Si un paquet a été altéré, son hash ne correspond plus et il est rejeté. On le commite. Pour un script chargé depuis un CDN, on ajoute l'intégrité directement dans la balise :

<script src="https://cdn.exemple.com/lib.js"
        integrity="sha384-…" crossorigin="anonymous"></script>

Minimiser. Chaque dépendance est de la surface d'attaque (la sienne, et toutes ses transitives). Avez-vous vraiment besoin d'une bibliothèque pour vérifier qu'un nombre est pair ? Moins de dépendances, moins de risque. Et avant d'en ajouter une : vérifiez le nom exact, le mainteneur, l'activité récente.

Le piège de l'audit. npm audit détecte les failles connues (les CVE publiées), mais pas un typosquat ni un paquet malveillant publié hier. Il ne remplace pas la lecture du nom et la vérification du mainteneur. Et un ^1.2.3 peut tirer une version compromise demain : le lockfile commité est là pour figer ce qui s'installe vraiment.

Défense en profondeur :

  1. auditer en continu (npm/composer audit, automatisé en CI, Dependabot) ;
  2. lockfile commité : versions épinglées + hash d'intégrité ;
  3. minimiser le nombre de dépendances ;
  4. vérifier nom exact, mainteneur et activité avant d'ajouter une dep ;
  5. SRI (integrity) sur les scripts chargés depuis un CDN.

Référence : OWASP Dependency-Check.

La méthode et l'arsenal du pentester

Ces réflexes défensifs sont votre armure. Voyons maintenant comment l'attaquant pense, pour comprendre ce qu'il cherche dans vos dépendances.

1. Cartographier les dépendances de la cible. Un package.json exposé, des en-têtes qui trahissent un framework, l'empreinte des bibliothèques front (un vieux jQuery dans le code source) : on liste ce que le site utilise, et en quelle version.

2. Croiser avec les CVE. Une version vulnérable connue, c'est souvent un exploit déjà public, prêt à l'emploi. C'est le chemin le plus rentable d'une attaque : on ne cherche pas une faille inédite, on cherche une lib pas à jour.

3. Viser la chaîne. Pour une attaque plus ambitieuse : typosquatter un paquet populaire (publier lodahs et attendre les fautes de frappe), ou pirater le compte d'un mainteneur par phishing pour publier une version piégée.

L'arsenal.

  • npm audit / composer audit / pip-audit : la première ligne, côté défense comme reconnaissance.
  • OWASP Dependency-Check / Snyk / Dependabot : scanner un projet entier contre les bases de failles, en continu.
  • retire.js : repère les bibliothèques JS front vulnérables (vieux jQuery, Angular, etc.).

Les incidents qui ont marqué

Trois histoires qui résument tout.

Log4Shell (2021). Une faille dans Log4j, une bibliothèque de logs Java « ennuyeuse » présente partout, a permis l'exécution de code à distance sur des millions de serveurs. La leçon : la lib la plus banale peut être le maillon le plus dangereux.

Des paquets npm compromis. Plusieurs paquets très populaires (par exemple event-stream, ua-parser-js) ont reçu une version malveillante, le temps de voler des cryptomonnaies ou d'installer un malware, avant d'être repérés. Tous étaient « de confiance »… jusqu'au jour où.

SolarWinds (2020). L'attaquant n'a pas compromis une dépendance, mais le build lui-même. Une porte dérobée a été glissée dans le logiciel officiel, puis distribuée à des milliers d'organisations, signée et d'apparence légitime. La compromission peut donc se situer bien en amont de vous.

Ce que ça révèle côté défense : vous héritez de la sécurité de tous ceux dont vous dépendez, et de ceux dont ils dépendent. On ne peut pas tout lire, mais on peut empiler les garde-fous : auditer en continu, épingler avec un lockfile, minimiser, vérifier l'intégrité. La confiance se vérifie, elle ne se présume pas (leçon 1).

La checklist dépendances

À mettre en place sur chaque projet.

  • Audit automatisé en CI : npm / composer / pip audit, Dependabot ou Renovate.
  • Lockfile commité : versions épinglées + hash d'intégrité.
  • Minimiser le nombre de dépendances (et leurs transitives).
  • Vérifier nom exact, mainteneur, activité avant d'ajouter une dep.
  • SRI (integrity) sur tous les scripts chargés depuis un CDN.
  • Surveiller les failles des libs utilisées (alertes de sécurité activées).

Les références. GitHub Dependabot (gratuit, intégré) ouvre les PR de mise à jour tout seul, OWASP Dependency-Check scanne un projet entier, et Snyk couvre tout l'écosystème. Pour le front : retire.js.

Rappel. Publier un typosquat ou viser le compte d'un mainteneur est une attaque réelle et illégale. On n'étudie ces techniques que pour s'en défendre, et on ne teste que ses propres systèmes (leçon 1).

You write 200 lines, you install a million

Throughout this course, we've been securing your code: your SQL queries, your tokens, your sessions, your cryptographic keys. This lesson widens the scope: what if the threat came from other people's code, the code you depend on without ever having written it?

You start a project, you write two hundred lines. Then you type npm install, and your node_modules folder fills with over a thousand packages written by strangers. In a modern app, 90% of what runs, you neither wrote nor read.

You write little, you install a huge amount of strangers' code Your code: ~200 lines you wrote + Your direct dependencies: ~20 chosen packages + + their dependencies (transitive): often 1000+
90% of what runs in your app, you didn't write. A flaw in any of it becomes yours.

The day one of those packages has a flaw, or turns malicious, it's your app that falls. The famous example: Log4Shell (2021). A flaw in a mundane Java logging library let attackers run code remotely on millions of servers. Many of those apps didn't even use the library directly: it was hidden inside their dependencies.

The rule is harsh but simple: a flaw in a dependency is your flaw. Your users don't care that the bug comes from a library; it's your site that leaks. It's A03 in OWASP 2025 (Software Supply Chain Failures), a category that grew serious enough to earn a top-3 spot.

The two dangers

1. The vulnerable dependency (the most common). A library you use has a published flaw (a CVE). If you don't update it, you stay exposed. It's passive: the flaw is known, documented, and the attacker looks for it in your app. It's the most rewarding path for them: no need to find a brand-new flaw, just an out-of-date library.

2. The supply chain attack (sneakier). Here, the attacker compromises the dependency itself:

  • Malicious package: a booby-trapped version is published, because the maintainer's account was hijacked, or the maintainer is malicious.
  • Typosquatting: a package named lodahs instead of lodash. One typo, and you install the malware.
  • Dependency confusion: you have an internal private package. The attacker publishes a public one with the same name and a higher version number. The manager picks theirs, thinking it's newer.

The common thread: npm install (or composer install) runs strangers' code on your machine and in production, with your privileges. Install scripts (postinstall) run on their own, without asking.

Beware the false comfort: "I installed a super-popular library, so it's safe". Popularity helps, but guarantees nothing: heavily used packages have been compromised (hijacked maintainer account). And you also inherit the dependencies of your dependencies (transitive), which you never chose. Trust is verified, not assumed (section 4).

Your turn to attack: install the booby-trapped package

Here are two projects. One is neglected (unverified dependencies), the other is under control (pinned versions, audit, verified names). Run npm install on each, and an npm audit, to see the difference. Everything is simulated.

Your project · dependencies

        

    
What just happened

Neglected project: npm install installed lodahs, a typosquat of lodash. Its postinstall script ran on its own and exfiltrated your secrets. One typo was enough to run malicious code, with your privileges. And lodash 4.17.4 carries a known flaw (CVE).

Project under control: the install runs from the lockfile (pinned versions + integrity hashes), npm audit is green, and the package names were verified. Notice: the audit does flag the vulnerable version, but it does not detect the typosquat. That's up to the human eye.

The fix: dependency hygiene

No magic formula, but a few reflexes that change everything.

Audit, continuously. Package managers can cross your dependencies against known flaws:

npm audit              # lists the CVEs of your dependencies (JS)
npm audit fix          # updates what it can without breaking
composer audit         # same on the PHP side

You automate all this in CI, and let a bot (Dependabot, Renovate) open the update pull requests.

Pin and verify integrity. The lockfile (package-lock.json, composer.lock) freezes exact versions and one hash per package. On each install, you get the same bytes. If a package has been tampered with, its hash no longer matches and it's rejected. Commit it. For a script loaded from a CDN, add integrity right in the tag:

<script src="https://cdn.example.com/lib.js"
        integrity="sha384-…" crossorigin="anonymous"></script>

Minimize. Every dependency is attack surface (its own, and all its transitive ones). Do you really need a library to check whether a number is even? Fewer dependencies, less risk. And before adding one: verify the exact name, the maintainer, the recent activity.

The audit trap. npm audit detects known flaws (published CVEs), but not a typosquat or a malicious package published yesterday. It doesn't replace reading the name and checking the maintainer. And a ^1.2.3 can pull a compromised version tomorrow: the committed lockfile is there to freeze what actually installs.

Defense in depth:

  1. audit continuously (npm/composer audit, automated in CI, Dependabot);
  2. committed lockfile: pinned versions + integrity hashes;
  3. minimize the number of dependencies;
  4. verify exact name, maintainer and activity before adding a dep;
  5. SRI (integrity) on scripts loaded from a CDN.

Reference: OWASP Dependency-Check.

The pentester's method and arsenal

Those defensive reflexes are your armour. Now let's think like the attacker, to understand exactly what they're hunting for in your dependencies.

1. Map the target's dependencies. An exposed package.json, headers that betray a framework, the fingerprint of front-end libraries (an old jQuery in the source): you list what the site uses, and in which version.

2. Cross-reference with CVEs. A known vulnerable version is often an already-public exploit, ready to use. It's the most rewarding path of an attack: you don't look for a brand-new flaw, you look for an out-of-date library.

3. Target the chain. For a more ambitious attack: typosquat a popular package (publish lodahs and wait for typos), or hijack a maintainer's account by phishing to publish a booby-trapped version.

The arsenal.

The incidents that marked everyone

Three stories that sum it all up.

Log4Shell (2021). A flaw in Log4j, a "boring" Java logging library found everywhere, allowed remote code execution on millions of servers. The lesson: the most mundane library can be the most dangerous link.

Compromised npm packages. Several very popular packages (for example event-stream, ua-parser-js) got a malicious version, long enough to steal cryptocurrency or install malware, before being caught. All were "trusted"… until the day they weren't.

SolarWinds (2020). The attacker compromised not a dependency, but the build itself. A backdoor was slipped into the official software, then distributed to thousands of organizations, signed and seemingly legitimate. The compromise can sit far upstream of you.

What this reveals for defense: you inherit the security of everyone you depend on, and of those they depend on. You can't read it all, but you can stack the guardrails: audit continuously, pin with a lockfile, minimize, verify integrity. Trust is verified, not assumed (lesson 1).

The dependencies checklist

To set up on every project.

  • Automated audit in CI: npm / composer / pip audit, Dependabot or Renovate.
  • Committed lockfile: pinned versions + integrity hashes.
  • Minimize the number of dependencies (and their transitive ones).
  • Verify exact name, maintainer, activity before adding a dep.
  • SRI (integrity) on every script loaded from a CDN.
  • Monitor the flaws of the libraries you use (security alerts enabled).

The references. GitHub Dependabot (free, built in) opens update PRs on its own, OWASP Dependency-Check scans a whole project, and Snyk covers the whole ecosystem. For the front end: retire.js.

Reminder. Publishing a typosquat or targeting a maintainer's account is a real and illegal attack. We only study these techniques to defend against them, and only test our own systems (lesson 1).

Prédisez avant de lire

Ton app utilise lodash 4.17.4, une version avec une faille connue (CVE). Tu ne lances jamais d'audit. Avant de dérouler : es-tu en sécurité ? Et que fait un npm install lodahs (avec la faute de frappe) ?

Voir la réponse

Non, tu es exposé. La faille de lodash 4.17.4 est publique et documentée : un attaquant qui voit cette version a souvent un exploit prêt. Une faille dans une dépendance est ta faille ; npm audit te l'aurait signalée, et la mise à jour l'aurait corrigée. Quant à npm install lodahs : c'est un typosquat de lodash. Tu installes un paquet d'un inconnu, dont le script postinstall s'exécute aussitôt avec tes droits, par exemple pour voler tes secrets. Une lettre de travers, et du code malveillant tourne chez toi.

Predict before reading on

Your app uses lodash 4.17.4, a version with a known flaw (CVE). You never run an audit. Before you expand: are you safe? And what does npm install lodahs (with the typo) do?

Show the answer

No, you're exposed. The lodash 4.17.4 flaw is public and documented: an attacker who sees this version often has a ready exploit. A flaw in a dependency is your flaw; npm audit would have flagged it, and updating would have fixed it. As for npm install lodahs: it's a typosquat of lodash. You install a stranger's package, whose postinstall script runs immediately with your privileges, for example to steal your secrets. One letter off, and malicious code runs on your machine.

🎯 Pratique

S'entraîner (clique pour ouvrir) :

💬 Ré-explique sans regarder
Ré-explique sans regarder

Avec tes mots : pourquoi « une faille dans une dépendance est ta faille », et qu'apportent le lockfile et l'audit (et qu'est-ce qu'ils ne couvrent pas) ?

Une bonne explication dit : ton app exécute surtout du code que tu n'as pas écrit (des centaines de dépendances, et leurs propres dépendances). Si l'une a une faille ou devient malveillante, c'est ton app qui tombe, et c'est toi le responsable aux yeux de tes utilisateurs. Le lockfile (package-lock, composer.lock) fige les versions exactes et des hash d'intégrité : tu installes les mêmes octets à chaque fois, et un paquet altéré est rejeté. L'audit (npm/composer audit) croise tes dépendances avec les failles connues (CVE) et te dit quoi mettre à jour. Mais ils ont des limites : l'audit ne voit pas un typosquat ni un paquet malveillant publié hier (pas encore de CVE). D'où le reste : minimiser les dépendances, vérifier le nom et le mainteneur avant d'installer.
🧠 Rappel libre
Rappel libre

Sans remonter : cite les deux dangers des dépendances, dis ce que fait npm audit, ce que fait le lockfile, et pourquoi minimiser les dépendances.

Deux dangers : (1) la dépendance vulnérable : une lib avec une faille connue (CVE) qu'on ne met pas à jour ; (2) l'attaque de la supply chain : la dépendance elle-même est piégée (paquet malveillant, typosquat, dependency confusion). npm audit croise tes dépendances avec les failles connues et te dit quoi mettre à jour. Le lockfile fige les versions exactes + des hash d'intégrité : mêmes octets à chaque install, paquet altéré rejeté. On minimise parce que chaque dépendance (et toutes ses transitives) est de la surface d'attaque : moins de deps, moins de code d'inconnus qui tourne chez toi.
⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

Tu dois vérifier si un nombre est pair. L'IA propose : « J'ajoute le paquet is-even avec npm install is-even : il est testé, maintenu et téléchargé des millions de fois, autant l'utiliser plutôt que réécrire la fonction. » Tu acceptes, ou tu rejettes ?

À rejeter. Vérifier qu'un nombre est pair, c'est une ligne : n % 2 === 0. Ajouter une dépendance pour ça, c'est payer un coût de sécurité sans raison : chaque paquet est de la surface d'attaque, traîne ses propres dépendances transitives, et devient une cible de supply chain (le célèbre is-even dépend même de is-odd !). « Populaire » ne veut pas dire « sûr pour toujours » : un compte mainteneur se pirate. La règle est de minimiser les dépendances : pour un one-liner trivial, on écrit la ligne soi-même. On réserve les libs aux vrais problèmes (crypto, parsing complexe, frameworks), justement ceux qu'il ne faut pas réécrire à la main.
Ton app utilise une lib avec une faille publiée (CVE), et tu ne mets jamais à jour. Es-tu en sécurité ?
À quoi sert le lockfile (package-lock.json, composer.lock) ?
npm audit détecte-t-il un paquet typosquatté (lodahs) ou malveillant publié hier ?
Pourquoi un simple npm install d'un paquet inconnu est-il risqué ?
Prochaine étape

Votre code et vos dépendances sont solides. Reste la dernière ligne droite : la mise en production. La leçon 14 ferme le cours avec les logs (sans fuiter), la gestion des erreurs, et la checklist OWASP de déploiement.

Leçon 14 : Logs, erreurs et déploiement →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement