Le 21 octobre, le jour même où PHP publiait ses correctifs d'urgence pour PHP2Shell, Node.js corrigeait sa propre faille de mémoire : Node2Shell (CVE-2026-111497). Un flux compressé en Brotli, malformé volontairement, peut faire écrire le runtime en dehors de sa mémoire, avec à la clé un crash, une fuite de mémoire ou, dans certaines conditions, une exécution de code.
Je suis CTO hands-on : j'architecture, je code, je structure l'équipe technique. Node.js, TypeScript et Next.js sont au cœur des stacks sur lesquelles je travaille. Cette faille est moins spectaculaire que PHP2Shell, mais elle est plus sournoise : votre exposition dépend d'un détail de configuration de votre parseur de requêtes et des URL que votre serveur va chercher. Voici comment savoir si vous êtes concerné, et quoi faire.
En bref : ce qu'il faut retenir sur Node2Shell
- Quoi : une écriture hors limites (out-of-bounds write) dans le décodeur Brotli que Node.js embarque dans son module
zlib. Bun et la librairie Brotli de Google sont aussi touchés. ^1 - Gravité : les notes divergent. Node.js la classe « High », Google, qui a attribué le CVE, la note 9.2 « Critical » en CVSS v4.0. ^2
- Versions corrigées : Node.js 22.23.4, 24.22.1 et 26.12.1, Bun 1.4.4. Node.js 20, 23 et 25 sont en fin de vie et ne seront pas corrigés. ^1
- Qui est exposé : uniquement le code qui décompresse du Brotli contrôlé par un attaquant. Express 5 avec
express.json()l'est par défaut, Express 4 non. Tout appelfetch()vers une URL fournie par un utilisateur l'est aussi. ^1 - Exploitation : aucune exploitation dans la nature confirmée au 23 octobre, faille absente du catalogue KEV de la CISA. Une exécution de code a été démontrée en laboratoire. ^1
- Action : mettre à jour Node.js. Il n'existe pas de contournement officiel. ^2
Ce qu'est Node2Shell, sans jargon
Brotli est un format de compression, très utilisé sur le web à côté de gzip. Quand un client envoie une requête avec l'en-tête Content-Encoding: br, ou quand un serveur répond en Brotli, quelqu'un doit décompresser ces données. Dans Node.js, c'est le module zlib, qui embarque le code C de la librairie Brotli de Google.
Le bug se trouve dans ce code C. Lors d'un changement de bloc dans le flux compressé, le décodeur ignore une erreur et continue d'écrire au-delà de son buffer. ^1 Un flux construit sur mesure par un attaquant permet donc d'écrire dans une zone mémoire qui ne devrait pas l'être.
Deux points sont importants pour comprendre le risque :
- La faille est dans le runtime, pas dans votre code ni dans vos dépendances npm. Mettre à jour le paquet npm
brotline sert à rien : le code vulnérable est compilé dans le binairenodelui-même. ^2 ^3 - Le bug a été corrigé discrètement bien avant l'annonce. Le correctif a été intégré au dépôt google/brotli le 27 février 2026, sans version publiée ni avis de sécurité. ^2 Les versions qui le contiennent n'ont été publiées que le 21 octobre.
La faille a été découverte et signalée par Khai Tran et Sylvie Mayer, de l'équipe OpenAI Codex Security. ^2 Comme le code de Brotli est réutilisé dans de nombreux autres logiciels, certains l'appellent aussi « HTTP2Shell ». ^4
Votre application Node.js est-elle exposée ?
C'est la question qui compte, et la réponse n'est pas « oui parce que vous utilisez Node ». Il faut deux conditions : une version de Node.js vulnérable, et un chemin par lequel des données Brotli contrôlées par un attaquant arrivent jusqu'au décodeur. ^2
| Situation | Exposé ? |
|---|---|
Express 5 avec express.json(), urlencoded(), text() ou raw() |
Oui, par défaut : body-parser 2.x décode les corps de requête en Brotli ^1 |
| Express 4 avec body-parser 1.x | Non par ce chemin : seuls deflate et gzip sont acceptés, le reste reçoit une erreur 415 ^1 |
| Koa (co-body, @koa/bodyparser, koa-body), Egg, Strapi | Oui, signalés comme atteignables ^1 |
Fastify avec @fastify/compress et décompression des requêtes |
Oui ^1 |
fetch() vers une URL contrôlée ou influencée par un utilisateur |
Oui, même avec Accept-Encoding: identity ^1 |
Appel direct à createBrotliDecompress() ou brotliDecompress() sur des données externes |
Oui ^2 |
Serveur node:http brut, sans décompression |
Non : il ne décode pas les corps Brotli tout seul ^1 |
Le cas fetch() est celui que les équipes oublient. Si votre backend récupère des URL fournies par vos utilisateurs (prévisualisation de liens, import de fichiers distants, webhooks sortants vers des URL configurées par le client, scraping), un attaquant n'a pas besoin de vous envoyer quoi que ce soit : il lui suffit de vous faire appeler un serveur qu'il contrôle, qui répond en Brotli malformé. Et ce chemin n'est bloqué ni par un WAF, ni par un reverse proxy placé devant vous. ^2
Et Next.js ?
Je n'ai pas trouvé d'analyse publique spécifique à Next.js à ce jour. Le raisonnement reste le même : une application Next.js qui tourne sur le runtime Node.js est concernée si ses route handlers ou son middleware décompressent des corps Brotli, ou si son code serveur appelle fetch() vers des URL influencées par un utilisateur. Dans tous les cas, c'est la version de Node.js qui fait tourner l'application qu'il faut mettre à jour, pas Next.js.
Quel est le risque réel ?
Il faut être honnête sur ce point, parce que les sources ne sont pas d'accord.
D'un côté, Imperva a démontré une exécution de code sur l'image Docker officielle de Node.js 24.22.0 (Linux arm64), sans modification, mais il lui a fallu plusieurs tentatives, et l'exploit n'est pas publié. Vercel a signalé une fuite de mémoire sur les versions 22, 24 et 26 non corrigées. ^1
De l'autre, la plupart des preuves de concept publiques ne provoquent qu'un crash du processus (déni de service). Une autre équipe n'a pas réussi d'exécution de code à l'aveugle avec la randomisation d'adresses active, et le commit du correctif décrit le bug comme peu sévère et difficile à exploiter. ^1
Ma lecture : le scénario le plus probable à court terme est le déni de service. Un serveur Express 5 qui redémarre en boucle parce qu'on lui envoie des requêtes Brotli piégées, c'est déjà un incident de production. Mais une fuite de mémoire sur un serveur qui manipule des tokens et des clés d'API n'est pas anodine non plus. Et l'histoire de PHP2Shell cette semaine montre qu'une exploitation « difficile » devient vite industrialisée. Je patche donc comme si l'exécution de code était acquise.
Chronologie
| Date (2026) | Événement |
|---|---|
| 27 février | Correctif intégré au dépôt google/brotli, sans version ni avis ^2 |
| 14 octobre | Node.js annonce une version de sécurité à venir ^1 |
| 21 octobre | Publication de Node.js 22.23.4, 24.22.1 et 26.12.1, puis de Brotli 1.0.10, 1.1.1 et 1.2.1 ^1 |
| 22 octobre | Publication du CVE, démonstration d'exécution de code par Imperva, sortie de Bun 1.4.4 ^2 |
| 23 octobre | Aucune exploitation dans la nature signalée, faille absente du catalogue KEV de la CISA ^1 |
Le plan d'action que j'applique
1. Inventorier tous les runtimes Node.js et Bun
Le serveur d'API n'est que la partie visible. Il faut aussi vérifier :
- les images Docker de tous les services, workers et jobs planifiés ;
- les fonctions serverless et les runtimes managés, en vérifiant auprès de votre plateforme quelle version de Node.js est réellement utilisée ;
- les machines de CI et les outils internes ;
- les binaires autonomes produits avec
bun build --compile, qui embarquent le runtime et doivent être recompilés. ^2
Attention aux distributions Linux : sur Debian, et peut-être sur d'autres distributions, Node.js peut utiliser la librairie Brotli du système. Dans ce cas, le correctif passe par la mise à jour du paquet Brotli, et le numéro de version de Node.js ne suffit pas à savoir si l'instance est protégée. Au 23 octobre, le suivi de sécurité de Debian n'indiquait encore aucune version corrigée. ^1
2. Mettre à jour et reconstruire
Montez sur Node.js 22.23.4, 24.22.1 ou 26.12.1 (ou une version ultérieure de la même branche), et Bun 1.4.4 ou plus. Reconstruisez les images à partir de bases corrigées, sans cache, puis vérifiez la version dans le conteneur qui tourne en production :
docker exec <conteneur> node --version
Si vous êtes encore sur Node.js 20, c'est le moment de migrer vers 22 ou 24 : il n'y aura pas de correctif. Pensez aussi à aligner le champ engines de votre package.json et vos fichiers .nvmrc, pour que les environnements de développement et de CI ne restent pas sur une version vulnérable.
3. Réduire l'exposition en attendant
Si vous ne pouvez pas redéployer aujourd'hui, deux mesures réduisent la surface d'attaque côté entrant : ^2
- Refuser
Content-Encoding: brau niveau du reverse proxy ou du load balancer, en comparant sans tenir compte de la casse et en gérant les listes commegzip, br. - Désactiver la décompression dans le parseur de corps de requête. Avec Express 5 :
app.use(express.json({ inflate: false }))
Ce réglage rejette aussi les corps compressés en gzip avec une erreur 415 : vérifiez d'abord qu'aucun de vos clients légitimes n'envoie de requêtes compressées. ^1
Aucune de ces deux mesures ne protège les réponses lues par fetch(). Pour ce chemin, auditez les appels vers des URL fournies par des utilisateurs, et isolez-les si possible dans un service séparé, sans secrets.
4. Limiter les dégâts d'une compromission
Ces mesures valent bien au-delà de Node2Shell : ^1
- faire tourner Node.js avec un utilisateur non privilégié ;
- éviter de mettre tous les secrets dans les variables d'environnement d'un processus exposé à Internet ;
- surveiller les processus shell lancés par Node.js et les connexions sortantes inattendues. Sysdig suggère notamment de détecter un crash de Node.js suivi d'une exécution de programme dans le même conteneur.
Au-delà de Node.js : une faille qui déborde
Le décodeur Brotli de Google est utilisé bien au-delà de Node.js. Les autres composants corrigés incluent : ^2 ^4
- Python : les paquets
brotli1.2.1 etbrotlicffi1.2.1.0. Les clients HTTPrequests/urllib3,httpxetaiohttpdécodent automatiquement le Brotli quand ces paquets sont installés. Aucune exécution de code n'a été démontrée côté Python à ce jour. - Envoy et Istio : concernés uniquement si le filtre de décompression Brotli est activé, ce qui n'est pas le cas par défaut dans Istio.
- Java et Ruby :
brotli4j1.24.0 et la gembrotli0.9.1. - curl et libcurl : dépendent d'une librairie Brotli, les copies embarquées nécessitent une nouvelle version de l'application.
Si votre stack mélange Node.js et des workers Python (un cas très courant dès qu'on fait de l'IA ou de la data), l'inventaire doit couvrir les deux.
Ce que Node2Shell dit de votre organisation technique
La même semaine, deux runtimes majeurs du web ont publié des correctifs critiques le même jour. Si votre équipe a dû choisir lequel traiter en premier faute de bras, c'est un signal.
Savoir lire son exposition réelle. Node2Shell est l'exemple parfait d'une faille où la bonne réponse n'est pas « tout le monde panique » mais « quels services décompressent du Brotli venant de l'extérieur ? ». Répondre à cette question en dix minutes suppose de connaître son architecture, ses dépendances et ses flux. C'est exactement ce qu'un audit technique doit produire.
Suivre la version du runtime comme une dépendance. Beaucoup d'équipes ont Dependabot ou Renovate sur leurs paquets npm, mais personne ne surveille la ligne FROM node:20 de leur Dockerfile. Le runtime est la dépendance la plus critique de votre application, et la plus souvent oubliée.
Pouvoir redéployer tous les services vite. Reconstruire une image et la déployer partout doit être une opération banale. Si ce n'est pas le cas, c'est un chantier prioritaire, au même titre que vos tests end-to-end : c'est ce qui vous permet de patcher sans avoir peur de casser la production.
Dans une startup sans direction technique, ces trois points n'ont souvent pas de responsable désigné. C'est le rôle que je tiens en CTO à temps partiel : connaître l'architecture de l'intérieur, lire les alertes et patcher moi-même, plutôt que de déléguer le sujet à un prestataire qui le découvrira la semaine suivante.
FAQ : la faille Node2Shell
Qu'est-ce que Node2Shell ?
Node2Shell est le nom donné à la vulnérabilité CVE-2026-111497, une écriture hors limites dans le décodeur Brotli intégré à Node.js, Bun et la librairie Brotli de Google. Un flux Brotli malformé peut provoquer un crash, une fuite de mémoire ou, dans certaines conditions, une exécution de code. Node.js l'a corrigée le 21 octobre 2026.
Quelles versions de Node.js sont corrigées ?
Node.js 22.23.4, 24.22.1 et 26.12.1 corrigent la faille, ainsi que Bun 1.4.4. Les branches en fin de vie, dont Node.js 20, 23 et 25, ne recevront pas de correctif : il faut migrer vers une branche maintenue comme 22 ou 24.
Mon application Express est-elle vulnérable ?
Une application Express 5 qui utilise express.json(), urlencoded(), text() ou raw() est exposée par défaut, car body-parser 2.x décode les corps de requête en Brotli. Une application Express 4 avec body-parser 1.x n'est pas exposée par ce chemin, mais elle peut l'être par d'autres middlewares ou par des appels fetch() vers des serveurs externes.
Faut-il mettre à jour le paquet npm brotli ?
Non, cela ne corrige pas Node2Shell. Le code vulnérable est compilé dans le runtime Node.js lui-même, pas dans le paquet npm brotli. Seule la mise à jour de Node.js (ou de la librairie Brotli du système, sur les distributions qui l'utilisent) corrige la faille.
Node2Shell est-elle exploitée activement ?
Au 23 octobre 2026, aucune exploitation dans la nature n'avait été confirmée et la faille ne figurait pas au catalogue KEV de la CISA. Une exécution de code a été démontrée en laboratoire par Imperva, et la plupart des preuves de concept publiques provoquent un crash du processus.
Un WAF ou un reverse proxy suffit-il à se protéger ?
Non. Refuser l'en-tête Content-Encoding: br au niveau du proxy bloque les requêtes entrantes, mais pas les réponses Brotli que votre serveur reçoit lorsqu'il appelle des URL externes avec fetch(). La mise à jour de Node.js reste la seule correction complète.
Ce que je recommande, en tant que CTO hands-on
Mettez à jour Node.js sur tous vos services cette semaine, en commençant par ceux qui tournent sous Express 5 ou qui appellent des URL fournies par vos utilisateurs. Vérifiez la version réellement en production, pas celle de votre .nvmrc. Et si vous êtes encore sur Node.js 20, considérez cette faille comme la date limite de votre migration.
Si vous avez aussi du PHP dans votre stack, mon article sur PHP2Shell détaille le plan d'action pour l'autre faille majeure de la semaine. Et si vous hébergez votre propre forge de code, lisez aussi la faille critique GitLab CVE-2026-110264, corrigée le même jour.