Axios 1.14.1 : anatomie d'une attaque supply chain npm en temps réel

· 7 min read

Le 31 mars 2026, un attaquant a compromis le compte d'un mainteneur Axios pour injecter un paquet malveillant. Retour technique sur l'attaque, pourquoi le lockfile ne suffit pas toujours, et la méthodologie de défense en profondeur.

SecuritynpmSupply ChainDevOpsNode.js

Le 31 mars 2026, un post sur X alerte la communauté : axios@1.14.1 est compromis. Un attaquant a pris le contrôle du compte npm du mainteneur principal et publié deux versions malveillantes (1.14.1 et 0.30.4) qui tirent plain-crypto-js@4.2.1 — un paquet malveillant créé le jour même. Aucune des deux versions n'apparaît dans les tags GitHub officiels du projet.

Axios, c'est +100 millions de téléchargements par semaine. L'un des paquets les plus utilisés de l'écosystème Node.js. Quand il tombe, c'est l'ensemble de la chaîne qui tremble.

Le principe, sans jargon

Imagine Bob et Alice. Les deux utilisent un Samsung Galaxy S22.

Jour 1 — Tout va bien

  Bob                          Alice
  [Samsung S22]                [Samsung S22]
  version: 14.0               version: 14.0
  -> tout fonctionne           -> tout fonctionne

Samsung publie une mise à jour. Bob et Alice ont activé les mises à jour automatiques.

Jour 2 — Mise à jour classique

  Samsung publie la version 14.1
  (correctif de sécurité légitime)

  Bob                          Alice
  [Samsung S22]                [Samsung S22]
  version: 14.0 -> 14.1       version: 14.0 -> 14.1
  -> OK, tout va bien          -> OK, tout va bien

Maintenant, un attaquant compromet le compte d'un développeur chez Samsung. Il publie une fausse mise à jour.

Jour 3 — L'attaque

  Attaquant publie la version 14.2
  (contient du code qui envoie tes contacts
   et tes photos à un serveur externe)

  Bob                          Alice
  [Samsung S22]                [Samsung S22]
  config: "accepter ^14.x"     config: "rester sur 14.1"
         |                            |
         v                            v
  version: 14.1 -> 14.2       version: 14.1 (pas de changement)
  -> COMPROMIS                 -> PROTEGEE

La seule différence : Alice a fixé sa version. Bob accepte "toute version 14.x".

Dans npm, c'est exactement la même chose. "axios": "^1.13.6" = "accepter toute version 1.x". "axios": "1.13.6" = "rester sur 1.13.6, point".

Ce qui s'est passé

L'attaque suit un schéma classique de dependency injection :

  1. Compromission du compte du mainteneur principal Axios sur npm — via un token npm longue durée compromis (le vecteur exact est encore sous investigation). Les autres mainteneurs n'ont pas pu révoquer l'accès car les permissions de l'attaquant dépassaient les leurs.
  2. Publication de axios@1.14.1 et axios@0.30.4 (les deux branches empoisonnées en 39 minutes) avec une nouvelle dépendance : plain-crypto-js@4.2.1
  3. plain-crypto-js est un paquet publié le jour même — nom volontairement proche de crypto-js (typosquatting) pour ne pas attirer l'attention
  4. Tout projet qui résout axios vers 1.14.1 ou 0.30.4 (via npm install avec un range ^1.x ou ^0.30.x) exécute le code malveillant

C'est la même mécanique que l'attaque event-stream de 2018, ua-parser-js de 2021, et colors/faker de 2022. Le vecteur change, le pattern reste.

Qui est impacté ?

Toute application qui a fait un npm install frais entre la publication de la version compromise et son retrait, avec un range permissif dans package.json :

// VULNERABLE — résout vers 1.14.1
"axios": "^1.7.9"
"axios": "^1.0.0"
"axios": ">=1.0.0"

// PROTEGE — ne résout jamais vers 1.14.1
"axios": "1.14.0"
"axios": "1.13.6"
"axios": "~1.13.6"

Pourquoi le lockfile ne suffit pas (toujours)

La première réaction est souvent : "j'ai un lockfile, je suis protégé". C'est partiellement vrai.

Quand le lockfile te protège

SituationProtégé ?
npm ci (CI/CD)Oui — toujours
npm install sans modificationOui
Clone frais + npm ciOui — si committé

Quand il ne te protège PAS

SituationPourquoi
npm install <nouveau-paquet>Re-résolution de l'arbre entier. Les deps avec ^ peuvent sauter à une version plus récente
npm updateConçu pour mettre à jour
Modification manuelle de package.json + installLockfile recalculé
Suppression du lockfilePlus de protection
Lockfile non committéChaque dev génère le sien
npm install --no-package-lockIgnore explicitement le lockfile

Le piège classique : un dev ajoute npm install dayjs. Sans le savoir, npm re-résout axios de 1.13.61.14.1. Le lockfile est mis à jour, committé, et toute l'équipe hérite du code malveillant. Un simple git diff package-lock.json aurait révélé l'apparition suspecte de plain-crypto-js.

Le champ integrity : ta dernière ligne de défense

Dans le lockfile, chaque paquet a un champ souvent ignoré :

"node_modules/axios": {
  "version": "1.13.6",
  "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
  "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr..."
}

Ce hash SHA-512 est calculé sur le tarball au moment du premier install. Si quelqu'un modifie le contenu d'un paquet sur le registry sans changer la version, npm ci refusera l'installation car le hash ne correspondra plus.

C'est une protection contre le registry tampering — pas contre la publication d'une nouvelle version malveillante (qui a son propre hash légitime).

La méthodologie de défense en profondeur

Niveau 1 — Le minimum vital (tout le monde devrait faire ça)

  • Lockfile committé dans git — toujours
  • npm ci en CI/CD, jamais npm install
  • Relire le diff du lockfile dans chaque PR

Niveau 2 — Équipe sérieuse

  • Versions fixes pour les dépendances critiques (HTTP, auth, paiement)
  • Renovate ou Dependabot — PRs automatiques de bump, avec diff visible et review
  • npm audit / pnpm audit dans le pipeline CI (en warning au minimum)

Niveau 3 — Sécurité renforcée

  • Socket.dev ou Snyk — analyse statique du code des dépendances
  • npm audit signatures — vérifie les attestations de provenance (SLSA/Sigstore)
  • Registry privé (Artifactory, Verdaccio) — proxy avec allowlist

npm audit / pnpm audit : utile mais insuffisant seul

Ces commandes interrogent la GitHub Advisory Database et comparent chaque paquet + version de ton lockfile contre les CVE connues (vulnérabilités publiées).

# npm
npm audit              # toutes les deps
npm audit --omit=dev   # seulement les deps de production

# pnpm
pnpm audit             # toutes les deps
pnpm audit --prod      # seulement les deps de production

Exemple de sortie :

┌─────────────────────┬──────────────────────────────────┐
│ moderate            │ Prototype Pollution in lodash    │
├─────────────────────┼──────────────────────────────────┤
│ Package             │ lodash                           │
│ Vulnerable versions │ <4.17.21                         │
│ Patched versions    │ >=4.17.21                        │
│ More info           │ https://ghsa.io/GHSA-xxxx        │
└─────────────────────┴──────────────────────────────────┘

Ce que ça détecte — et ce que ça ne détecte pas

npm audit détecte ?
CVE publiée sur une dépendanceOui
Paquet avec une vulnérabilité connue et patchéeOui
Attaque zero-day (paquet malveillant sans CVE encore)Non
Code malveillant dans un postinstall scriptNon
Typosquatting (paquet au nom similaire)Non

Conclusion : npm audit détecte les vulnérabilités après qu'elles ont été signalées. C'est une couche de défense utile mais réactive — elle n'aurait pas détecté l'attaque axios au moment de la publication. Pour la détection proactive, il faut des outils comme Socket.dev qui analysent le comportement du code (appels réseau, eval, accès filesystem).

Pourquoi c'est la combinaison des trois qui fonctionne

Aucune de ces couches ne suffit seule. Chacune couvre l'angle mort des deux autres :

Scénario d'attaqueVersions fixesLockfile + npm cinpm audit
Nouvelle version malveillante (axios@1.14.1)Bloque — tu restes sur 1.13.6Bloque — le lockfile fixe 1.13.6Pas de CVE encore
Dev fait npm install dayjs et axios saute à 1.14.1Bloque — version fixée, npm ne peut pas bumperLe lockfile est mis à jour silencieusementPas de CVE encore
CVE connue sur une dep existanteTu restes sur la version vulnérableIdemDétecte et alerte
Dev supprime le lockfile et fait npm installBloque — version exacte, pas de rangePlus de lockfilePas de CVE encore

La ligne 2 est la plus importante : sans version fixée, un simple npm install dayjs peut re-résoudre tout l'arbre de dépendances. Le lockfile est mis à jour, committé, et npm ci en CI n'y verra rien — il installera fidèlement le nouveau lockfile compromis.

Les versions fixes sont la seule couche qui protège quand le lockfile est re-généré.

Versions fixes  → empêche la résolution silencieuse vers une version malveillante
npm ci          → garantit que la CI utilise exactement le lockfile committé
npm audit       → rattrape les CVE connues sur les versions fixées

Le sweet spot pour une petite équipe

Si tu es founding engineer ou en petite équipe, voici le ratio effort/protection optimal :

1. Lockfile committé                              → gratuit
2. npm ci en CI                                   → 5 min de config
3. Versions fixes sur deps réseau (axios, auth0)  → 2 min par dep
4. Dependabot activé                              → 10 min one-shot
5. Relire le diff lockfile dans les PRs           → discipline

Cinq actions. Pas quinze. Et ça couvre l'attaque d'aujourd'hui.

La vraie leçon

L'outillage aide, mais la discipline fait la différence.

La plupart des développeurs impactés ont fait npm install, le lockfile s'est mis à jour silencieusement avec axios@1.14.1 + plain-crypto-js, et ils ont committé sans regarder le diff.

Le réflexe à ancrer : chaque fois que le lockfile change, se poser la question — est-ce que je comprends pourquoi ces dépendances ont changé ?

Si un paquet inconnu apparaît dans le diff et que tu ne l'as pas ajouté explicitement : c'est un signal d'alerte.

Checklist incident supply chain

Pour référence, voici la checklist à dérouler quand un paquet est signalé compromis :

□ grep `plain-crypto-js`, `axios@1.14.1` et `axios@0.30.4` dans tous les lockfiles
□ Vérifier la version résolue de la dep directe
□ Vérifier node_modules/ si installé localement
□ Si impacté : fixer la version, supprimer node_modules, npm ci
□ Rotation des secrets/tokens du poste de dev
□ Vérifier les images Docker déployées en production
□ Monitorer les logs réseau pour des connexions suspectes

Rappel : syntaxe des versions npm

SyntaxeRange autorisé1.7.9 résout vers 1.14.1 ?
1.7.9ExacteNon
~1.7.9≥1.7.9 <1.8.0Non
^1.7.9≥1.7.9 <2.0.0Oui
*ToutOui
>=1.0.0Tout depuis 1.0.0Oui

L'écosystème npm transporte une dette structurelle : des registres mutables, des comptes mainteneurs sans 2FA obligatoire, et des millions de projets qui font confiance aveugle à ^. Chaque attaque supply chain est un rappel que la sécurité de ton application dépend de la discipline avec laquelle tu gères tes dépendances — pas seulement de la qualité de ton code.