Une dépendance est du code tiers qui s'exécute avec les droits de votre application, et parfois dès son installation. Cette leçon montre comment fermer les trois portes les plus utilisées par les attaquants, le typosquatting, la dependency confusion et les scripts d'installation, puis comment figer et vérifier ce que vous installez sur npm, PyPI et les modules Go. Elle s'adresse à un développeur ou un DevOps qui gère des dépendances au quotidien. Les commandes ont été jouées le 1er octobre 2026 avec npm 10.9.9, pip-audit 2.9.0, Go 1.25 et cosign 3.1.3.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Reconnaître le typosquatting, la dependency confusion et les scripts d'installation malveillants
- Désactiver les scripts npm et inspecter ceux d'un paquet avant de les autoriser
- Figer les dépendances par lockfile,
npm ciet hash pip - Protéger les paquets internes avec une portée npm et un index unique
- Vérifier la provenance d'un paquet npm, d'un paquet PyPI ou d'un binaire signé
- Rattacher ces mesures aux exigences SOCLE-FRN-OSS-3 et SOCLE-INT-OSS-2 du référentiel SOCLE
Les vecteurs d'attaque
Section intitulée « Les vecteurs d'attaque »Trois techniques reviennent dans la plupart des compromissions de dépendances. Elles ne visent pas votre code mais votre gestionnaire de paquets, et s'appuient sur ses comportements par défaut : installer ce qu'on lui demande, choisir une version, exécuter les scripts qu'un paquet déclare.
Typosquatting
Section intitulée « Typosquatting »Le typosquatting exploite les fautes de frappe. L'attaquant publie un paquet dont le nom ressemble à celui d'un paquet populaire, et attend qu'un développeur se trompe en tapant la commande.
| Paquet légitime | Typosquat | Différence |
|---|---|---|
lodash | lodahs | Inversion de lettres |
requests | request | Singulier et pluriel |
tensorflow | tensorfow | Lettre manquante |
@angular/core | angular-core | Format différent |
Le scénario est banal : on veut requests, le client HTTP Python très
répandu, et l'on tape une lettre de moins. Le paquet malveillant s'installe
sans erreur, embarque souvent le vrai code du paquet imité pour ne pas
éveiller les soupçons, et exécute à l'installation un script qui exfiltre les
identifiants présents sur le poste. PyPI et npm retirent régulièrement de
tels paquets, mais certains restent en ligne plusieurs jours avant d'être
détectés.
# Une lettre de moins, un autre paquetpip install request # ❌ nom proche, autre auteurpip install requests # le paquet attenduDependency confusion
Section intitulée « Dependency confusion »La dependency confusion vise les paquets internes d'une entreprise.
L'attaquant découvre leur nom, par une fuite de package.json, un message
d'erreur ou du code publié, puis publie sur le registre public un paquet du
même nom, dans une version très élevée comme 99.0.0. Si le gestionnaire de
paquets peut trouver ce nom sur le registre public, il risque de préférer la
version la plus haute. Le schéma suivant montre ce mécanisme avec pip, et la
parade qui le neutralise.
Le risque dépend de l'écosystème, et c'est le point que les guides simplifient le plus souvent :
| Situation | Risque |
|---|---|
pip avec --extra-index-url | Élevé : pip consulte tous les index et retient la version la plus haute |
npm, nom interne sans portée (acme-utils) | Élevé : le registre par défaut est le registre public |
| Registre proxy qui fusionne public et privé (dépôt virtuel) | Élevé : le proxy peut servir la version publique la plus haute |
npm, nom à portée (@acme/utils) associée à un registre privé | Faible : une portée se résout sur un seul registre |
| npm, portée non réservée sur npmjs.com | Élevé : l'attaquant peut créer l'organisation @acme et y publier |
En 2021, le chercheur Alex Birsan a démontré l'attaque contre Microsoft, Apple et PayPal en publiant des paquets aux noms de leurs dépendances internes : son code s'est exécuté sur leurs machines de build, et il a reçu plus de 130 000 dollars de primes. Les parades, détaillées par écosystème plus bas, tiennent en deux mesures : portée npm réservée et associée au registre privé, index pip unique.
Scripts d'installation malveillants
Section intitulée « Scripts d'installation malveillants »Les gestionnaires de paquets savent exécuter du code au moment de l'installation. C'est utile pour compiler un module natif, et c'est aussi une porte d'entrée classique des logiciels malveillants : le code s'exécute avant même que vous n'ayez importé le paquet.
| Gestionnaire | Mécanisme | Exécute du code à l'installation ? |
|---|---|---|
| npm | preinstall, install, postinstall | Oui, par défaut |
| pip | construction d'une distribution source (setup.py, backend de build) | Oui, si aucun wheel n'est disponible |
| Go | aucun | Non |
Protection npm
Section intitulée « Protection npm »npm est une cible de choix, par sa taille et par ses scripts d'installation. Les protections ci-dessous vont de la plus simple à la plus robuste, et se cumulent.
Désactiver les scripts d'installation
Section intitulée « Désactiver les scripts d'installation »Un paquet peut déclarer des scripts que npm lance pendant npm install.
Mesuré : esbuild 0.24.2 déclare un postinstall (node install.js), et
npm install l'exécute. Avec ignore-scripts=true, le même npm install
n'exécute plus aucun script et installe les 72 paquets du projet de test.
# Aucun script de paquet n'est exécuté à l'installationignore-scripts=true# Ou ponctuellement, sans fichier de configurationnpm install --ignore-scriptsAvant d'autoriser un paquet qui a réellement besoin de ses scripts, comme un
module natif, lisez ce qu'ils font. npm view affiche les scripts déclarés
sans rien installer. N'utilisez pas npm run-script --dry-run pour cela :
il ne liste que les scripts de votre projet et n'apprend rien sur un
paquet tiers.
npm view esbuild@0.24.2 scripts# { postinstall: 'node install.js' }Vérifier un paquet avant de l'installer avec npq
Section intitulée « Vérifier un paquet avant de l'installer avec npq »npq s'intercale avant npm install et confronte le paquet demandé à
plusieurs signaux : paquet connu comme malveillant, présence de scripts,
âge, popularité, mainteneurs. Mesuré sur le typosquat lodahs, il bloque
l'installation et cite la base qui le signale :
npx npq@3 install lodahs --dry-runPackages with issues found:
--- Package: lodahs@latest --- ERROR: Supply Chain Security - Malicious package found: https://snyk.io/vuln/npm:lodahs
Summary:
- Total packages: 1 - Total errors: 1 - Total warnings: 0Épingler les versions exactes
Section intitulée « Épingler les versions exactes »Par défaut, npm enregistre des plages de versions (^4.17.0 signifie
« 4.17.0 ou plus récent, sans changer de version majeure »). Si une version
malveillante 4.18.0 est publiée, le prochain npm install sans lockfile la
prend. Mesuré : l'option save-exact vaut false par défaut ; passez-la à
true pour que npm enregistre la version exacte.
npm config set save-exact true| Notation | Signification | Risque |
|---|---|---|
"4.17.21" | Exactement cette version | Maîtrisé |
"^4.17.0" | De 4.17.0 à 4.x.x | Mises à jour mineures automatiques |
"~4.17.0" | De 4.17.0 à 4.17.x | Correctifs automatiques |
"*" | N'importe quelle version | ❌ À proscrire |
Lockfile obligatoire, et npm ci en CI
Section intitulée « Lockfile obligatoire, et npm ci en CI »Le lockfile package-lock.json fige la version exacte et l'empreinte
(integrity) de chaque dépendance, directe ou transitive. Il se commite
toujours. En CI, npm ci l'applique strictement là où npm install peut le
réécrire. Mesuré : sans lockfile, npm ci refuse de s'exécuter
(EUSAGE) ; avec un lockfile qui ne correspond plus au package.json, il
refuse aussi, au lieu de l'adapter en silence.
npm ci# npm error code EUSAGE# npm error `npm ci` can only install packages when your package.json and# package-lock.json or npm-shrinkwrap.json are in sync.| Commande | Comportement | Usage |
|---|---|---|
npm install | Peut mettre à jour les dépendances et le lockfile | Poste de développement |
npm ci | Installe exactement le lockfile, échoue s'il diverge | CI/CD, production |
Registre privé pour les paquets internes
Section intitulée « Registre privé pour les paquets internes »Contre la dependency confusion, deux mesures se complètent. Donnez à vos
paquets internes une portée (@acme/…) et associez-la à votre registre
privé dans le .npmrc : npm ne cherchera jamais un paquet @acme/* ailleurs.
Puis réservez l'organisation @acme sur npmjs.com, même vide, pour
qu'aucun tiers ne puisse y publier.
# Tous les paquets @acme/* viennent UNIQUEMENT du registre privé@acme:registry=https://npm.acme-corp.example/
# Le reste vient du registre publicregistry=https://registry.npmjs.org/Si votre registre privé est un proxy qui agrège le registre public, vérifiez qu'il ne sert jamais la version publique d'un nom interne : c'est par ce type de dépôt virtuel que la confusion revient.
Protection PyPI
Section intitulée « Protection PyPI »PyPI exige désormais l'authentification à deux facteurs de tous ses mainteneurs et propose la publication sans jeton par Trusted Publishers. Il reste exposé au typosquatting, et pip exécute du code quand il doit construire un paquet depuis ses sources.
Auditer les vulnérabilités avec pip-audit
Section intitulée « Auditer les vulnérabilités avec pip-audit »pip-audit résout les dépendances d'un requirements.txt et les confronte
aux bases de vulnérabilités (OSV et la base d'avis de PyPI). Mesuré sur un
fichier qui ne contient que requests==2.25.1 : il trouve 13
vulnérabilités dans 3 paquets, requests et ses dépendances idna et
urllib3, et sort en code 1, ce qui suffit à faire échouer un job de CI.
pip install pip-audit==2.9.0pip-audit -r requirements.txtFound 13 known vulnerabilities in 3 packagesName Version ID Fix Versions-------- ------- ------------------- ------------requests 2.25.1 PYSEC-2023-74 2.31.0requests 2.25.1 PYSEC-2026-1873 2.32.0Éviter l'exécution de code à l'installation
Section intitulée « Éviter l'exécution de code à l'installation »pip exécute du code quand il construit un paquet depuis une distribution
source (.tar.gz). Un wheel (.whl) est déjà construit : son
installation n'exécute pas de code. L'option --only-binary :all: interdit les
distributions source.
pip install --only-binary :all: requestsLa contrepartie est mesurable : pyyaml==5.3.1 n'a pas de wheel pour Python
3.12, et pip répond No matching distribution found. Ce refus est voulu ; il
signale un paquet à auditer ou à remplacer par une version qui publie des
wheels.
| Type de paquet | Extension | Exécute du code à l'installation ? |
|---|---|---|
| Distribution source | .tar.gz | Oui (construction) |
| Wheel | .whl | Non |
Un seul index pour les paquets internes
Section intitulée « Un seul index pour les paquets internes »Avec extra-index-url, pip consulte tous les index et retient la version
la plus haute : un 99.0.0 publié sur PyPI l'emporte sur votre 1.2.0
interne. La parade est de ne déclarer qu'un seul index, votre registre
privé, qui sert de proxy vers PyPI pour les paquets publics et réserve les
noms internes.
[global]# Un seul index : le proxy privé, qui sert aussi les paquets publicsindex-url = https://pypi.acme-corp.example/simple/Le fichier se place dans ~/.config/pip/pip.conf sous Linux,
~/Library/Application Support/pip/pip.conf sous macOS et
%APPDATA%\pip\pip.ini sous Windows.
Épingler avec hash
Section intitulée « Épingler avec hash »Épingler une version ne protège pas contre un fichier remplacé sous le même
numéro. Le hash SHA-256 du fichier, ajouté à la ligne, le permet : pip
refuse d'installer un fichier dont l'empreinte diffère. Mesuré : le hash du
wheel requests-2.31.0-py3-none-any.whl est bien celui ci-dessous, et un hash
faux fait échouer l'installation avec Expected sha256 … Got ….
requests==2.31.0 \ --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003fpip-compile, de pip-tools, génère ces lignes pour toutes les dépendances,
directes et transitives, à partir d'un requirements.in qui ne liste que vos
dépendances directes.
pip install pip-toolspip-compile --generate-hashes requirements.inProtection Go modules
Section intitulée « Protection Go modules »Go a été conçu avec la chaîne d'approvisionnement en tête. Il n'exécute
aucun code à l'installation d'un module, et vérifie par défaut chaque
module contre une base de checksums publique. Mesuré sur Go 1.25 :
GOPROXY vaut https://proxy.golang.org,direct et GOSUMDB vaut
sum.golang.org.
| Risque | npm | pip | Go |
|---|---|---|---|
| Scripts à l'installation | Oui, par défaut | Oui, pour une distribution source | Non |
| Empreinte des dépendances | Dans le lockfile | Optionnelle (--hash) | go.sum, obligatoire |
| Base de checksums publique | Non | Non | sum.golang.org |
Vérifier l'intégrité des modules
Section intitulée « Vérifier l'intégrité des modules »Le fichier go.sum contient l'empreinte de chaque module, et
go mod verify vérifie que le cache local leur correspond. Mesuré : sur un
cache intact, la commande répond all modules verified ; après l'ajout d'une
ligne dans un fichier du module github.com/google/uuid, elle répond dir has been modified et sort en code 1.
go mod verify# all modules verified
# Après altération du cache# github.com/google/uuid v1.6.0: dir has been modified (/go/pkg/mod/github.com/google/uuid@v1.6.0)Un cache altéré se purge et se retélécharge, et go.sum se commite toujours :
go clean -modcachego mod downloadModules privés : GOPRIVATE
Section intitulée « Modules privés : GOPRIVATE »Par défaut, Go demande chaque module au proxy public et vérifie son
empreinte contre sum.golang.org : un module interne y est introuvable, et sa
requête révèle son nom. La variable GOPRIVATE liste les préfixes de
modules privés, que Go ne demande ni au proxy ni à la base de checksums
(documentation de go help private).
export GOPRIVATE=github.com/acme-corp/*,gitlab.acme.example/*
# Avec un proxy privé (Athens, Artifactory), combinez les deuxexport GOPROXY=https://goproxy.acme.example,https://proxy.golang.org,directVérifier la provenance des dépendances
Section intitulée « Vérifier la provenance des dépendances »Les protections précédentes réduisent la surface d'attaque, mais ne répondent pas à une question : ce paquet a-t-il été construit à partir du code source que l'on peut auditer ? Dans la porte dérobée de xz-utils, en 2024, le script qui activait la charge malveillante ne figurait que dans les archives de publication, pas dans le dépôt Git : relire le code du dépôt ne suffisait pas à la voir.
Une provenance répond à cette question. C'est une attestation signée qui relie l'artefact à son dépôt, à son commit et à la plateforme qui l'a construit. Le cadre SLSA (Supply-chain Levels for Software Artifacts) en définit les niveaux d'exigence, et le format in-toto en décrit les attestations.
npm : signatures de registre et provenance
Section intitulée « npm : signatures de registre et provenance »npm distingue deux preuves. Toutes les versions du registre public portent une
signature de registre, qui prouve que le paquet n'a pas été modifié depuis
sa publication. La provenance, elle, n'existe que si le mainteneur a publié
avec npm publish --provenance depuis une CI compatible. npm install ne
vérifie ni l'une ni l'autre : c'est npm audit signatures qui le fait.
npm audit signaturesaudited 72 packages in 0s
72 packages have verified registry signatures
1 package has a verified attestationMesuré sur le projet de test (express, lodash, esbuild) : les 72
paquets ont une signature de registre valide, mais un seul porte une
attestation de provenance. La provenance reste minoritaire : servez-vous-en
comme critère de choix pour vos dépendances critiques, pas comme condition
bloquante générale.
PyPI : Trusted Publishers et attestations
Section intitulée « PyPI : Trusted Publishers et attestations »Les Trusted Publishers, introduits par PyPI en 2023, permettent à un projet de publier depuis un workflow de CI identifié, par jeton OIDC éphémère, sans jeton d'API stocké. Attention à ce qu'ils garantissent : ils n'empêchent pas un mainteneur de publier par ailleurs avec un jeton d'API tant que celui-ci existe. Depuis fin 2024, PyPI expose aussi des attestations pour les fichiers publiés de cette façon, interrogeables par son API.
curl -s -H 'Accept: application/vnd.pypi.integrity.v1+json' \ https://pypi.org/integrity/pydantic/2.11.7/pydantic-2.11.7-py3-none-any.whl/provenanceMesuré : pydantic 2.11.7 et sampleproject 4.0.0 renvoient une attestation
qui nomme le dépôt et le workflow de publication (pydantic/pydantic,
ci.yml). requests 2.32.4 renvoie 404 : ce paquet très populaire ne
publie pas d'attestation. Ne supposez donc jamais qu'un paquet connu est publié
par un Trusted Publisher : vérifiez-le.
Binaires : vérifier une signature Sigstore
Section intitulée « Binaires : vérifier une signature Sigstore »Un binaire téléchargé depuis une page de publication se vérifie avec sa
signature. Le format le plus courant est le bundle Sigstore
(.sigstore.json), vérifié avec cosign. Mesuré sur cosign 3.1.3, qui
signe ses propres binaires :
# 1. Télécharger le binaire et son bundlegh release download v3.1.3 --repo sigstore/cosign \ --pattern 'cosign-linux-amd64' --pattern 'cosign-linux-amd64.sigstore.json'
# 2. Vérifier la signature ET l'identité du signatairecosign verify-blob cosign-linux-amd64 \ --bundle cosign-linux-amd64.sigstore.json \ --certificate-identity keyless@projectsigstore.iam.gserviceaccount.com \ --certificate-oidc-issuer https://accounts.google.com# Verified OKL'option --certificate-identity est décisive : sans elle, n'importe quelle
signature Sigstore valide passerait, et vous auriez vérifié la cryptographie
sans vérifier l'origine. Le certificat du bundle mesuré porte bien l'identité
keyless@projectsigstore.iam.gserviceaccount.com. En cas d'échec, n'exécutez
pas le binaire.
Choisir une dépendance
Section intitulée « Choisir une dépendance »Ajouter une dépendance revient à faire confiance à du code et à des personnes que vous ne connaissez pas. Quelques critères se vérifient en quelques minutes, avant l'ajout, et évitent les dépendances abandonnées ou mal tenues.
| Critère | Comment vérifier | Repère |
|---|---|---|
| Score OpenSSF Scorecard | API publique ou scorecard --repo=… | Au-dessus de 5 sur 10 |
| Dernière publication | Page du paquet | Moins de 6 mois |
| Mainteneurs actifs | Contributeurs du dépôt | Au moins 2 |
| Provenance | npm audit signatures, API d'intégrité PyPI | Présente si possible |
| Réactivité aux failles | Avis de sécurité du dépôt | Correctifs en semaines, pas en mois |
L'API publique de Scorecard évite d'installer l'outil. Mesuré le 1er octobre
2026 pour expressjs/express : score 8.9 sur 10 (Scorecard v5.5.0), avec
Code-Review, Maintained et SAST à 10. Deux contrôles valent -1,
c'est-à-dire non évaluables, pas nuls : Signed-Releases faute de publication
GitHub, et Branch-Protection faute de droits suffisants pour la lire. Un
score se lit donc contrôle par contrôle, pas seulement globalement.
curl -s https://api.securityscorecards.dev/projects/github.com/expressjs/express \ | jq '{score, date, checks: [.checks[] | {name, score}]}'Le guide OpenSSF Scorecard détaille l'outil et son intégration en CI.
Bonnes pratiques universelles
Section intitulée « Bonnes pratiques universelles »Ces six règles résument la leçon et valent pour les trois écosystèmes. Elles se mettent en place dans cet ordre, de la plus rapide à la plus structurante.
-
Commiter le lockfile :
package-lock.json,go.sum,requirements.txtavec hashes. -
Installer strictement en CI :
npm ci,pip install --require-hashes,go mod verify. -
Auditer régulièrement :
npm audit,pip-audit, et un scanner SCA en CI. -
Isoler les paquets internes : portée npm réservée, index pip unique,
GOPRIVATE. -
Désactiver les scripts d'installation, et autoriser au cas par cas après lecture.
-
Vérifier la provenance des dépendances critiques avant de les adopter.
Outils de détection
Section intitulée « Outils de détection »Les outils ci-dessous complètent les gestionnaires de paquets. Les deux premiers ont été mesurés dans cette leçon ; les suivants élargissent la couverture à la base OSV ou à la détection de comportements suspects dans le code des paquets.
| Outil | Écosystème | Détecte |
|---|---|---|
| npq | npm | Paquets malveillants connus, scripts, signaux faibles |
| pip-audit | PyPI | Vulnérabilités connues |
| OSV-Scanner | Multi | Vulnérabilités et paquets malveillants référencés dans OSV |
| Socket | npm, PyPI | Comportements suspects dans le code des paquets |
| Snyk | Multi | Vulnérabilités et licences |
À retenir
Section intitulée « À retenir »- Le typosquatting exploite une faute de frappe ; npq bloque les paquets malveillants connus.
- La dependency confusion frappe pip avec
extra-index-urlet npm sans portée réservée. ignore-scripts=truecoupe les scripts npm ;npm view <paquet> scriptsles montre avant.- Le lockfile se commite, et
npm cirefuse un lockfile absent ou désynchronisé. - Un hash pip refuse un fichier remplacé ;
go mod verifydétecte un cache altéré. npm installne vérifie rien :npm audit signaturescontrôle signatures et provenance, encore rare.- Une signature se vérifie avec l'identité du signataire, jamais sans.
- Preuves attendues par SOCLE-FRN-OSS-3 et INT-OSS-2 : des versions verrouillées et une liste de registres autorisés.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de cette leçon est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : OpenSSF Scorecard : évaluer un projet avant d'en dépendre
Continuer le cours : Varnish Artifact Firewall : contrôler l'entrée des dépendances
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
Ressources externes
Section intitulée « Ressources externes »Ces documentations de référence approfondissent chaque écosystème.