Faille Node.js : correctif Node2Shell et vérification de la version de Node.js (WordPress)

Node2Shell est le surnom de CVE-2026-111497, une faille de Node.js dont le correctif a été publié le 21 octobre 2026 ; son titre officiel décrit une écriture hors limites dans le décodeur Brotli de Google. Comme la décompression de données Brotli forgées peut mener à une exécution de code à distance, la première chose à faire est de vérifier les versions de Node.js, Bun et libbrotli de votre serveur et de les mettre à jour vers les versions corrigées.
Sur un serveur Nginx + Apache sous WordPress 7.1.3, j'ai vérifié quels paquets utilisent le décodeur Brotli et par quel chemin les requêtes distantes de WordPress passent par libbrotli.
1️⃣ Node2Shell, la faille Node.js : comment fonctionne le défaut et qui est concerné
Le nom Node2Shell est un surnom donné par l'équipe de recherche de la société de sécurité Imperva dans un article du 22 octobre. Le titre de l'avis de nodejs.org est [Brotli decoder out-of-bounds write permits remote code execution]. Le défaut se trouve dans le code source du décodeur Brotli de Google intégré à Node.js (c/dec/decode.c) : modifier le code de votre application ne suffit donc pas à le bloquer.
D'après la fiche NVD, le décodeur de Google Brotli 1.0.3 à 1.2.0 écrit au-delà de la fin d'un tampon lorsqu'il décompresse un flux forgé, ce qui corrompt la mémoire d'état adjacente du décodeur. Avec ce défaut, un attaquant distant non authentifié peut lire le contenu de la mémoire du serveur, interrompre le service et exécuter du code arbitraire.
Il existe deux évaluations de gravité. L'avis de nodejs.org la classe High, et Google, qui a émis la CVE, lui attribue 9,2 (Critical) selon CVSS 4.0. Au 23 octobre, la NVD n'a pas encore terminé sa propre analyse, il n'y a donc pas encore de score NVD. Les types de faiblesse sont une valeur de retour non vérifiée (CWE-252) et une écriture hors limites (CWE-787).
D'après l'avis de sécurité de nodejs.org, toutes les branches prises en charge de Node.js (22.x, 24.x et 26.x) sont concernées, et les versions corrigées sont 22.23.4, 24.22.1 et 26.12.1. Le même avis précise que les branches en fin de vie sont toujours considérées comme concernées à chaque avis de sécurité.
Tout programme qui utilise le même décodeur est concerné, même en dehors de Node.js. Le 21 octobre, Google a publié trois versions de la bibliothèque Brotli qui corrigent la même CVE : 1.0.10, 1.1.1 et 1.2.1. Bun a inscrit Brotli dans la liste des correctifs de sécurité de l'annonce de la 1.4.4 le 22 octobre, et une issue de son dépôt précise que cette version contient le correctif qui met à jour brotli.
| destination | Versions concernées | Version corrigée | Source |
|---|---|---|---|
| Node.js | Toutes les branches prises en charge 22.x · 24.x · 26.x · branches en fin de vie | 22.23.4 · 24.22.1 · 26.12.1 | Avis de sécurité de nodejs.org (2026-10-21) |
| Bibliothèque Brotli de Google | 1.0.3 ~ 1.2.0 | 1.0.10 · 1.1.1 · 1.2.1 | Fiche NVD · versions de google/brotli (2026-10-21) |
| Bun | Brotli dans la liste des correctifs de sécurité de l'annonce de la 1.4.4 | 1.4.4 | Annonce de Bun (2026-10-22) |
| Paquet libbrotli de la distribution | Paquets construits à partir de Brotli 1.0.3 à 1.2.0 | La version indiquée par l'avis de sécurité de la distribution (aucune au 2026-10-23) | Page du suivi de sécurité de chaque distribution |
Node.js ne décompresse pas de lui-même les données Brotli qu'il reçoit. D'après l'avis de sécurité de l'hébergeur Vercel, les points exposés sont ceux où une application ou une bibliothèque décompresse des données compressées venues de l'extérieur. C'est le cas quand le fetch() intégré reçoit une réponse br et quand Express 5 ou body-parser 2 décompresse un corps de requête br ; le serveur HTTP par défaut, qui se contente de recevoir, ne décompresse rien.
Au 23 octobre, aucune exploitation réelle n'a été confirmée. Cette CVE ne figure pas dans le catalogue des vulnérabilités exploitées connues de la CISA (KEV, version du 21 octobre). Le niveau d'exploitation que la CISA attribue dans la fiche CVE est passé le 23 octobre à [preuve de concept (PoC) publique].
C'est l'emplacement du décodeur qui détermine ce qu'il faut faire. Les binaires officiels de Node.js intègrent le décodeur dans l'exécutable, mais certaines versions compilées, comme le paquet nodejs de Debian, utilisent la libbrotli du système. Les distributions Linux installent le même code source sous forme de paquet libbrotli, partagé par des programmes comme curl.
2️⃣ Les sites WordPress et PHP aussi concernés : le chemin par lequel les requêtes distantes décompressent les réponses avec libbrotli
Même un serveur WordPress sans Node.js contient le même décodeur, installé sous forme de paquet libbrotli. Au 23 octobre, WordPress, curl et PHP n'ont fait aucune déclaration officielle sur cette CVE, et elle ne figure pas non plus dans la liste officielle des vulnérabilités de curl. Cette section ne fait donc que confirmer la structure, à partir du code du cœur, de la documentation de curl et de mesures sur un serveur de test.
Sur le serveur Nginx + Apache mesuré, cinq paquets exigeaient la bibliothèque du décodeur (libbrotlidec), et celui qui est relié à WordPress est libcurl.
rpm -q --whatrequires 'libbrotlidec.so.1()(64bit)' | sed 's/-[0-9].*//'freetype
brotli-devel
libjxl
libcurl
libsouplibcurl exige libbrotli pour décompresser les réponses HTTP compressées. D'après la documentation officielle de curl, une libcurl compilée avec la bibliothèque brotli décompresse automatiquement les réponses dont le Content-Encoding est br. L'extension curl de PHP appelle directement cette libcurl.
ldd /usr/lib64/libcurl.so.4 | grep brotli libbrotlidec.so.1 => /lib64/libbrotlidec.so.1 (0x00007f32803ed000)
libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00007f327f858000)php -i | grep BROTLIBROTLI => YesLa version de libcurl utilisée par WordPress s'affiche à la ligne [Version de cURL] lorsque vous dépliez la section [Serveur] dans l'onglet [Informations] de [Outils › Santé du site] dans l'administration. Cette ligne n'indique pas si brotli est pris en charge : vérifiez-le avec des commandes serveur comme ci-dessus.
La ligne [Version de cURL] de l'image affiche la version de libcurl avec la bibliothèque SSL avec laquelle elle a été compilée. D'après la documentation de curl, br est pris en charge depuis la 7.57.0 : si cette valeur est inférieure, libcurl ne peut pas décompresser les réponses br. Si elle est égale ou supérieure, vérifiez avec des commandes serveur si brotli a été inclus dans la compilation.
Lorsque la bibliothèque Requests du cœur de WordPress envoie une requête vers une adresse externe, elle définit la valeur [CURLOPT_ENCODING] du handle curl sur une chaîne vide. D'après la documentation de curl, une chaîne vide signifie accepter toutes les méthodes de compression prises en charge par cette libcurl. Si le serveur distant envoie une réponse compressée en br, c'est donc la libbrotli du serveur WordPress qui la décompresse.
grep -n CURLOPT_ENCODING wp-includes/Requests/src/Transport/Curl.php110: curl_setopt($this->handle, CURLOPT_ENCODING, '');
212: curl_setopt($this->handle, CURLOPT_ENCODING, 'none');La ligne 110 est la valeur par défaut de toutes les requêtes, et la ligne 212 ne s'exécute que lorsqu'une requête dont la décompression a échoué est renvoyée sans compression. Il n'y a aucun moyen de l'atteindre directement depuis l'extérieur : il faudrait d'abord prendre le contrôle d'un serveur dont WordPress récupère les réponses, par exemple pour la vérification des mises à jour du cœur et des extensions, oEmbed ou les flux RSS.
Le déroulement commence par des données compressées en br venues de l'extérieur. Le défaut se déclenche à l'étape où le fetch() de Node.js, Express 5, le fetch() de Bun ou la libbrotli appelée par la libcurl de WordPress les décompresse. Le résultat mène à une exécution de code sur le serveur. Les versions corrigées bloquent ce déroulement : ce qu'il faut faire, c'est remplacer le décodeur par une version corrigée.
Le mod_brotli d'Apache ne lie que la bibliothèque de l'encodeur (libbrotlienc), qui compresse les réponses : une configuration qui active la compression avec ce module ne se trouve donc pas sur le chemin du défaut du décodeur.
ldd /usr/lib64/httpd/modules/mod_brotli.so | grep brotli libbrotlienc.so.1 => /lib64/libbrotlienc.so.1 (0x00007f34b4b79000)
libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00007f34b4a79000)3️⃣ Vérifier la version de Node.js : Node.js, Bun et libbrotli sont-ils sur le serveur, et en quelle version
Commencez par faire l'inventaire de ce qui est installé sur le serveur. Pour Node.js, regardez la version avec [node -v] : si la valeur est inférieure à 22.23.4, 24.22.1 ou 26.12.1 selon sa branche, elle est concernée.
| Votre environnement | Commande de vérification | Ce qui est corrigé |
|---|---|---|
| Installeur nodejs.org · nvm · image Docker officielle | [node -v] | Node.js lui-même (22.23.4 · 24.22.1 · 26.12.1) |
| nodejs de la distribution · ligne libbrotli dans ldd | [ldd $(command -v node)] | Paquet libbrotli |
| nodejs de la distribution · résultat ldd 0 | [ldd $(command -v node)] | Paquet nodejs (après l'avis de sécurité de la distribution) |
| Node.js Selector en hébergement mutualisé (CloudLinux alt-nodejs) | Annonce de l'hébergeur · numéro de release du paquet | Paquet déployé par l'hébergeur + redémarrage de l'application |
| Bun | [bun --version] | Bun 1.4.4 ou ultérieur |
| Pas de Node.js (WordPress seul) | Vérifier libcurl avec [ldd] | Paquet libbrotli + redémarrage de php-fpm · d'ici là, mesure temporaire |
| Hébergement infogéré | Annonce de l'hébergeur · demande au support | Paquets mis à jour par l'hébergeur |
Sur un PC (Windows ou macOS), réinstallez avec l'installeur de la version corrigée de nodejs.org ; avec nvm, regardez la version avec [nvm ls] puis réinstallez par numéro de branche, comme [nvm install 24]. Les tags 22, 24 et 26 de l'image Docker officielle node ont été reconstruits avec les versions corrigées le 22 octobre : récupérez à nouveau l'image, reconstruisez et déployez. L'avis de Vercel recommande aussi de passer aux versions corrigées les images de conteneur que vous construisez vous-même, les environnements de CI et les environnements de développement locaux.
Sur un serveur où vous avez installé Node.js via un stream de module dnf, [node -v] affiche une version de la branche de ce stream ; même si la branche est la même, il faut mettre à jour si les derniers chiffres n'atteignent pas la version corrigée.
node -vv24.xx.xPour savoir si Node.js utilise la libbrotli du système ou son décodeur intégré, vérifiez avec [ldd]. Si la sortie contient une ligne libbrotlidec, la version compilée utilise la bibliothèque partagée ; si elle affiche 0, comme ci-dessous, le décodeur est intégré et seule la mise à jour de Node.js lui-même corrige le problème.
ldd $(command -v node) | grep -c brotli0Pour Bun, vérifiez la version avec [bun --version] et mettez à jour si elle est inférieure à 1.4.4.
Sur les systèmes RPM, regardez la version du paquet libbrotli avec [rpm -q libbrotli]. Les numéros de version des paquets sont attribués par la distribution : ne les comparez pas directement aux numéros de version de Google, mais à la version publiée par l'avis de sécurité de la distribution pour ce numéro de CVE.
rpm -q libbrotlilibbrotli-1.0.x-x.el9_x.x86_64Pour savoir si la distribution a publié un avis, utilisez [dnf updateinfo info --cve CVE-2026-111497]. Si la sortie est vide, ce dépôt n'a pas encore d'avis pour cette CVE.
dnf updateinfo info --cve CVE-2026-111497Un paquet qui reprend seulement le correctif sans changer le numéro de version garde le numéro de CVE dans son journal des modifications. On vérifie donc avec [rpm -q --changelog libbrotli | grep CVE-2026-111497] : si une ligne s'affiche, le paquet est corrigé.
rpm -q --changelog libbrotli | grep CVE-2026-111497Au 23 octobre, les paquets libbrotli de Debian, Ubuntu, Red Hat et AlmaLinux n'ont pas encore de version corrigée pour cette CVE. Alpine n'a de version corrigée que pour son paquet nodejs ; la source est la page du suivi de sécurité de chaque distribution. Pour l'instant, la marche à suivre est donc de surveiller la publication de l'avis de sécurité de la distribution et de mettre à jour dès qu'il paraît.
Sous Debian et Ubuntu, le paquet s'appelle libbrotli1, et j'ai vérifié les mêmes points sur un serveur de test Debian 12 d'un hébergeur infogéré. Sur ce serveur aussi, libcurl liait libbrotlidec et l'extension curl de PHP prenait en charge brotli. Le journal des modifications se consulte avec [zcat /usr/share/doc/libbrotli1/changelog.Debian.gz | grep CVE-2026-111497].
dpkg -l libbrotli1 | tail -1ii libbrotli1:amd64 1.1.0-2+0~20240305.8+debian12~1.gbp62df71 amd64 library implementing brotli encoder and decoder (shared libraries)ldd /usr/lib/x86_64-linux-gnu/libcurl.so.4 | grep brotli libbrotlidec.so.1 => /lib/x86_64-linux-gnu/libbrotlidec.so.1 (0x00007fa2baac6000)
libbrotlicommon.so.1 => /lib/x86_64-linux-gnu/libbrotlicommon.so.1 (0x00007fa2b9cb9000)4️⃣ Mettre à jour Node.js : passer à la version corrigée et vérifier à nouveau
Chez les hébergeurs infogérés et mutualisés, c'est l'hébergeur qui met à jour les paquets du serveur : vérifiez via ses annonces ou son support si Node.js et libbrotli ont été corrigés. Si vous gérez vous-même le serveur, passez Node.js à la version corrigée de l'avis de nodejs.org, et Bun à la 1.4.4 ou ultérieure avec [bun upgrade].
Le Node.js installé depuis un stream de module de la distribution se met à jour avec une commande du type [dnf upgrade nodejs] une fois la version corrigée arrivée dans ce dépôt. Sur un serveur où Node.js a été téléchargé et installé directement depuis nodejs.org, remplacez-le par la version corrigée au même endroit. Les branches en fin de vie ne reçoivent pas de version corrigée : passez à une branche prise en charge, 22, 24 ou 26.
Le Node.js Selector des hébergements mutualisés (CloudLinux alt-nodejs) embarque sa propre copie de brotli : mettre à jour la libbrotli du système ne le corrige pas. D'après l'annonce de CloudLinux, le nouveau binaire ne s'exécute qu'une fois le paquet déployé par l'hébergeur en place et l'application redémarrée. La même annonce précise que pour les versions en fin de vie, la valeur de [node --version] ne change pas : on vérifie alors le numéro de release du paquet.
Pour le paquet libbrotli, dès que l'avis de sécurité de la distribution paraît, mettez ce paquet à jour et redémarrez les processus qui ont la bibliothèque chargée en mémoire pour qu'ils passent à la nouvelle bibliothèque. Les bibliothèques partagées sont lues au démarrage d'un processus : sur un serveur WordPress, le redémarrage de php-fpm fait partie de la même étape.
Après la mise à jour, vérifiez que [node -v] affiche la version corrigée ou ultérieure et que [rpm -q libbrotli] affiche la version de l'avis de la distribution. Vérifiez la présence du numéro de CVE dans le journal des modifications avec [rpm -q --changelog libbrotli | grep CVE-2026-111497]. Si vous avez redémarré php-fpm, contrôlez aussi l'état du service et la réponse du site.
systemctl is-active php-fpmactivecurl -s -o /dev/null -w '%{http_code}\n' 'https://example.com/?r=1'2005️⃣ Mesure temporaire avant le correctif : désactiver la décompression br dans les requêtes distantes de WordPress
La page CVE de Red Hat recommande de configurer les applications pour qu'elles n'acceptent pas, ou ne décompressent pas automatiquement, la compression br provenant de sources non fiables.
Même avant la sortie des paquets corrigés, vous pouvez bloquer dès maintenant le chemin côté WordPress. WordPress fournit le hook [http_api_curl], qui transmet le handle curl à chaque requête distante. Définir [CURLOPT_ENCODING] sur null à cet endroit désactive la décompression automatique de libcurl.
Placer le fichier ci-dessous dans le dossier [wp-content/mu-plugins/] l'applique à toutes les requêtes distantes ; le nom du fichier n'est qu'un exemple. Pour revenir en arrière, supprimez ce fichier.
<?php
// no-brotli-decode.php — WordPress distant dans les requêtes libcurl Auto décompression Désactivé(libbrotli correctif version publiée jusqu'à)
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
add_action( 'http_api_curl', function ( $handle ) {
curl_setopt( $handle, CURLOPT_ENCODING, null );
}, 99 );On pourrait croire qu'en limitant [CURLOPT_ENCODING] à [gzip, deflate], br disparaît. Cette valeur ne fait que retirer br de l'en-tête Accept-Encoding de la requête : si le serveur distant répond en br, libcurl décompresse quand même ce qu'il reçoit. Sur le site de test, en récupérant httpbin.org/brotli avec cette même valeur, la réponse au Content-Encoding br a été décompressée et lisible telle quelle.
D'après la documentation de curl, la seule valeur qui désactive la décompression automatique est null : avec null, l'en-tête Accept-Encoding n'est pas envoyé et le corps reçu n'est pas décompressé non plus. Les réponses gzip et deflate sont décompressées directement par la bibliothèque Requests de WordPress et restent lisibles ; seules les réponses br restent non décompressées.
Ce hook est celui que WordPress a branché sur le point [curl.before_send], que Requests appelle juste avant d'exécuter curl. Il est appelé à la ligne 174 de Curl.php et curl s'exécute à la ligne 205 : la chaîne vide que le cœur a placée à la ligne 110 est donc écrasée entre les deux.
Vérifiez l'application avec le fichier de test ci-dessous. httpbin.org est une adresse de test qui renvoie les en-têtes de la requête reçue ainsi que des réponses compressées en br et en gzip : on voit d'un coup ce que WordPress envoie et ce qu'il décompresse.
<?php
// br-check.php — WordPress distant requête quelle compression envoie si elle décompresse Vérifier(httpbin.org test Adresse)
$h = json_decode( wp_remote_retrieve_body( wp_remote_get( 'https://httpbin.org/headers' ) ), true );
echo 'Accept-Encoding: ', isset( $h['headers']['Accept-Encoding'] ) ? $h['headers']['Accept-Encoding'] : '(none)', PHP_EOL;
foreach ( array( 'brotli', 'gzip' ) as $p ) {
$r = wp_remote_get( 'https://httpbin.org/' . $p );
$readable = strpos( wp_remote_retrieve_body( $r ), ': true' ) !== false;
echo $p, ': Content-Encoding=', wp_remote_retrieve_header( $r, 'content-encoding' ), ' / ', strlen( wp_remote_retrieve_body( $r ) ), ' bytes / ', ( $readable ? 'readable' : 'not decoded' ), PHP_EOL;
}Avant d'ajouter le fichier, Accept-Encoding contient br et la réponse br est décompressée et lisible.
wp eval-file br-check.phpAccept-Encoding: deflate, gzip, br
brotli: Content-Encoding=br / 316 bytes / readable
gzip: Content-Encoding=gzip / 317 bytes / readableAprès l'ajout du fichier, l'en-tête Accept-Encoding disparaît, la réponse br reste non décompressée et la réponse gzip reste lisible.
wp eval-file br-check.phpAccept-Encoding: (none)
brotli: Content-Encoding=br / 199 bytes / not decoded
gzip: Content-Encoding=gzip / 272 bytes / readableUne fois le paquet libbrotli corrigé de la distribution installé et php-fpm redémarré, supprimez ce fichier pour revenir à l'état d'origine.
🔢 FAQ & contenus recommandés
Sécurité
Faille OpenSSH CVE-2024-6387
Comment j'ai bloqué temporairement la faille OpenSSH, puis appliqué le correctif et vérifié.
Sécurité
Sécurité WP : sécuriser wp-admin
Les réglages qui protègent l'interface d'administration en changeant l'URL de connexion, en limitant les connexions et en bloquant XML-RPC.
Serveur
Mise à jour d'Alma Linux : 9.4 → 9.8
Les étapes suivies pour passer d'AlmaLinux 9.4 à 9.8 et les points à vérifier.
ℹ️ Mention d'affiliation
Le contenu de ce site contient des liens d'affiliation. Lorsqu'un visiteur achète un produit ou un service via l'un de ces liens, le site perçoit une commission du vendeur. Le montant payé par l'acheteur(il baisse lors des promotions ↓), lui, n'augmente pas. Les prix, remises et stocks indiqués correspondent à la date de rédaction et peuvent différer ; vérifiez-les donc auprès du vendeur avant d'acheter. Les produits sont choisis et évalués selon nos propres critères, et les commissions n'influencent ni l'ordre de présentation ni le contenu des évaluations.