GHSA-673q-mjm6-r5xr : RCE sans authentification dans les fonctions « use cache » de Next.js
GHSA-673q-mjm6-r5xr est classée critique, CVSS v4.0 9.5, dans la GitHub Advisory Database : un attaquant non authentifié peut exécuter du code sur un serveur Next.js 16 utilisant App Router avec Cache Components et une fonction use cache renvoyant plusieurs tableaux typés. L’examen de la base classe la faille comme une désérialisation de données non fiables (CWE-502) dans le protocole de transport React Server Components embarqué par Next.js. Corrigé dans Next.js 16.4.1, sans solution de contournement disponible.
En bref
- Un attaquant non authentifié peut obtenir une exécution de code à distance sur une application Next.js 16 qui active Cache Components et possède une fonction
use cacherenvoyant plusieurs tableaux typés. L’examen de la GitHub Advisory Database classe la faille sous CWE-502, désérialisation de données non fiables. - Toutes les versions Next.js 16 de 16.0.0 à 16.4.0 sont affectées. La GitHub Advisory Database classe la faille comme critique, CVSS v4.0 9.5, exploitable sur le réseau sans authentification ni interaction utilisateur.
- Aucune preuve de concept publique n’existe, aucune exploitation réelle n’est connue au 23 octobre 2026 et la faille ne figure pas dans le catalogue CISA Known Exploited Vulnerabilities.
- Mettez immédiatement à jour vers Next.js 16.4.1. Vercel indique qu’aucune solution de contournement n’existe et recommande une mise à jour immédiate même si votre application ne possède pas de fonctions
use cacherenvoyant des tableaux typés.
Suis-je concerné ?
Vous êtes concerné si votre application utilise Next.js 16.0.0 à 16.4.0 avec App Router et Cache Components activés. Cache Components est le modèle de cache de Next.js 16 : les fonctions marquées avec la directive use cache stockent et restaurent leurs résultats entre les requêtes. La condition de déclenchement du bulletin est une fonction use cache qui renvoie plusieurs tableaux typés, comme des valeurs Uint8Array ou Float64Array.
| Branche | Versions affectées | Version corrigée |
|---|---|---|
| Next.js 16.x | 16.0.0 – 16.4.0 | 16.4.1 |
Vérifiez la version installée :
# Installed Next.js version
npm ls next
node -e "console.log(require('next/package.json').version)"
# Lock file check (package-lock.json v2/v3 records the package under "node_modules/next")
grep -A1 '"node_modules/next"' package-lock.json
# Container image check
docker run --rm <your-image> node -e "console.log(require('next/package.json').version)"
Vérifiez ensuite si votre code active la fonctionnalité affectée. Cache Components est une configuration facultative, et use cache apparaît directement dans le code serveur :
# Cache Components enabled in the Next.js config
grep -n "cacheComponents" next.config.*
# Any use of the cache directive across the project, including src/ and shared libraries
grep -rn "use cache" --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" --exclude-dir=node_modules --exclude-dir=.next .
Une seule fonction renvoyant deux tableaux typés ou plus satisfait la condition de déclenchement publiée.
Comment corriger le problème ?
Mettez Next.js à jour vers 16.4.1 ou ultérieur :
npm install next@16.4.1
# or: pnpm add next@16.4.1 | yarn add next@16.4.1
Il n’existe aucune solution de contournement. Le bulletin de Vercel est explicite : effectuez immédiatement la mise à jour, même si votre application ne possède pas de telles fonctions use cache. La même version corrige aussi la RCE sans authentification dans Image Optimization publiée le même jour (GHSA-2464-fhr4-w9xw, causée par la faille librsvg en amont CVE-2026-96889). Une seule mise à jour traite donc les deux problèmes.
Quelle est la cause racine ?
Next.js repose sur React Server Components (RSC), où le serveur rend la sortie des composants sous forme d’un flux de blocs sérialisés, généralement appelé données flight. Relire ce flux est une désérialisation : l’environnement d’exécution parcourt les blocs, résout les références et reconstruit objets, promesses, flux, tableaux typés et références de fonctions serveur. Avec Cache Components, la valeur de retour d’une fonction use cache est stockée et restaurée via ce même transport. Le désérialiseur traite donc des données provenant d’une entrée de cache. Le bulletin classe la faille sous CWE-502, désérialisation de données non fiables.
Vercel n’a pas publié le chemin d’exploitation précis ; l’analyse ci-dessous provient donc de la lecture du correctif. Le patch, commit a4cea6a intitulé « Improve Server Component transport protocol », remplace les environnements react-server-dom-webpack et react-server-dom-turbopack embarqués dans les douze variantes de build par des versions qui durcissent la frontière de désérialisation à quatre endroits.
Premièrement, l’ancien désérialiseur résolvait chaque entrée de modèle sans vérifier si une propriété nommée then allait faire passer un objet ordinaire pour une promesse. JavaScript traite tout objet doté d’une propriété then appelable comme une promesse, un thenable ; le code qui attend cet objet ou chaîne des opérations dessus appelle cette fonction. Le nouveau code refuse cette confusion dès l’analyse :
// after the fix, in the bundled react-server-dom client and server runtimes
function createModel(response, model, parentObject, key) {
return "then" === key && "function" === typeof model ? null : model;
}
Avant le correctif, createModel renvoyait le modèle sans modification. Un objet désérialisé contenant une fonction then restait donc actif dans le graphe d’objets reconstruit et pouvait être appelé partout où l’environnement d’exécution ou le code applicatif le traitait comme une promesse.
Deuxièmement, la résolution des références serveur, qui associe une référence sérialisée à une véritable fonction serveur et peut aboutir au chargement de code par requireModule, valide désormais son entrée :
function loadServerReference(response, metaData, parentObject, key) {
if ("then" === key) return null;
var id = metaData.id;
if ("string" !== typeof id) return null;
...
Avant le correctif, metaData.id atteignait resolveServerReference et requireModule sans vérification de type.
Troisièmement, l’environnement d’exécution a cessé d’exposer son objet de réponse interne via le graphe de blocs. L’ancien code stockait directement l’objet de réponse actif comme chunk.reason sur chaque bloc de modèle résolu. Un parcours des propriétés des objets reconstruits pouvait l’atteindre, puis atteindre les mécanismes de désérialisation à travers lui. Le nouveau code masque la réponse derrière un symbole généré à l’exécution sur un objet conteneur : un parcours ordinaire des propriétés ne peut donc pas l’atteindre.
// before: new ReactPromise("resolved_model", value, response)
// after:
var RESPONSE_SYMBOL = Symbol();
function createResolvedModelChunk(response, value) {
var holder = {};
return new ReactPromise("resolved_model", value,
((holder[RESPONSE_SYMBOL] = response), holder));
}
Quatrièmement, le transport sépare désormais ce qui partageait auparavant un même espace de clés. Les payloads sérialisés circulent comme entrées form-data dont la clé est un préfixe suivi d’un identifiant de bloc. Les changements, par aspect :
- L’ancien désérialiseur résolvait l’état interne par une recherche brute parmi toutes les entrées :
response._formData.data.get(response._prefix + id). Les lectures de tableaux typés faisaient de même viaresponse._formData.data.get(key).arrayBuffer(). - Le nouveau code classe les entrées par type à leur arrivée : blocs de flux sous une clé préfixée par le préfixe de champ suivi de « S », blocs de chaînes et blobs binaires dans leurs propres maps. Les lectures de tableaux typés passent par
getBackingBlob(response._formData, key). - Côté sérialisation, le client écrit les entrées de flux sous
formFieldPrefix + "S" + streamIdau lieu du simpleformFieldPrefix + streamId. - La sérialisation des tableaux typés copie un tableau de plus de 4096 octets (2048 octets dans les builds navigateur) lorsque sa vue ne commence pas au début du tampon sous-jacent, ne couvre pas tout ce tampon ou si le tampon est redimensionnable.
En une phrase : le désérialiseur faisait confiance à la structure des entrées relues, acceptait des objets se faisant passer pour des promesses, des références serveur avec des types d’identifiants arbitraires et des entrées dont la nature dépendait seulement de leur clé. Le correctif ajoute une vérification de type et de catégorie à chacun de ces points de confiance.
Comment la faille est-elle exploitée ?
Aucune preuve de concept n’est publique, et Vercel n’a pas documenté le chemin de requête exact. Ce qui suit décrit donc la classe d’exploitation présentée par le bulletin et le correctif, pas une recette pas à pas.
- L’application active Cache Components et définit une fonction
use cachequi renvoie plusieurs tableaux typés. Chaque tableau devient un bloc binaire distinct dans le payload sérialisé, ce qui correspond à la condition requise indiquée par Vercel. - Une requête non authentifiée atteint l’application. Le vecteur CVSS (réseau, aucun privilège, aucune interaction utilisateur) et la classification CWE-502 décrivent la surface d’attaque : l’entrée de l’attaquant atteint des données traitées par le désérialiseur flight.
- Dans ces données, l’attaquant fournit des entrées conçues pour exploiter les lacunes de confiance fermées par le correctif : un objet doté d’une fonction
thenque l’environnement appellera comme une promesse ; ou une référence serveur dontidn’est pas une chaîne mais se résout vers un contenu choisi par l’attaquant ; ou une entrée qui utilise l’espace de clés commun pour être traitée comme un bloc interne. - Le désérialiseur résout l’entrée spécialement conçue. Via la résolution des références serveur, le chemin peut atteindre
requireModule, la fonction qui associe une référence sérialisée à du code serveur réel. - La référence choisie par l’attaquant s’exécute avec les privilèges du processus serveur Next.js : c’est une exécution de code à distance.
La condition des tableaux typés correspond directement à ce chemin : la partie sérialisation du correctif modifie l’émission des tableaux dont les vues ne couvrent pas tout le tampon sous-jacent, et sa partie désérialisation modifie la recherche de leurs blobs sous-jacents. Ce code s’exécute lorsque plusieurs tableaux typés partagent un même payload.
Déroulement de l’attaque
route Cache Components"] --> B["Une fonction use cache renvoie
plusieurs tableaux typés"] B --> C["Le protocole flight sérialise le résultat
en entrées form-data par blocs"] C --> D["Une entrée influencée par l’attaquant partage
l’espace de clés des blocs internes"] D --> E["Le désérialiseur résout l’entrée comme
modèle interne ou référence serveur"] E --> F["La confusion thenable ou l’identifiant
de référence non validé est accepté"] F --> G["requireModule résout la
référence, le code s’exécute"]
Les vérifications manquantes se situent en D et E : avant le correctif, rien ne validait la nature d’une entrée ni le type de l’identifiant d’une référence serveur avant sa résolution.
Que peut faire un attaquant ?
Une exploitation réussie exécute du code arbitraire sous l’identité du processus serveur Next.js. Ce processus détient généralement des secrets de déploiement, des connexions aux bases de données et des identifiants de services, et peut lire et réécrire tout ce que l’application peut atteindre. Le vecteur CVSS v4.0 (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:N) attribue un impact élevé à la confidentialité et à l’intégrité sur le système vulnérable comme sur les systèmes suivants, et un impact élevé à la disponibilité sur le seul système vulnérable.
Les conditions nécessaires à l’attaquant, dans la limite de ce qu’indique le bulletin :
- L’application utilise Next.js 16.0.0 à 16.4.0 avec App Router et Cache Components activés.
- L’application possède une fonction
use cachequi renvoie plusieurs tableaux typés. - L’attaque est non authentifiée et ne nécessite aucune interaction utilisateur, mais le vecteur CVSS indique des conditions d’attaque présentes, ce qui correspond au déclenchement dépendant de la fonctionnalité ci-dessus.
La consigne de Vercel d’effectuer la mise à jour même sans ces fonctions suggère de ne pas considérer cette condition comme une protection fiable : le code applicatif évolue et le transport est une infrastructure partagée.
Pourquoi le correctif fonctionne-t-il ?
Le commit de correction a4cea6a remplace l’environnement React Server Components embarqué pour chaque cible de build : serveur et client, webpack et turbopack, développement et production. Chacun des quatre changements ferme une lacune de confiance à la frontière de désérialisation :
-function createModel(response, model) {
- return model;
+function createModel(response, model, parentObject, key) {
+ return "then" === key && "function" === typeof model ? null : model;
}
function loadServerReference(response, metaData, parentObject, key) {
+ if ("then" === key) return null;
+ var id = metaData.id;
+ if ("string" !== typeof id) return null;
...
Une fonction then présente dans les données désérialisées est supprimée : aucun objet reconstruit ne peut donc se faire passer pour une promesse. Une référence serveur doit porter un identifiant de type chaîne avant toute résolution ou tout chargement. L’objet de réponse est hors de portée du parcours des propriétés. Enfin, le classement des entrées et la sérialisation corrigée des tableaux typés font que la nature d’un bloc découle de son emplacement de stockage, et non d’une clé choisie par l’attaquant.
Le correctif est-il complet ? Il ferme toutes les lacunes qu’il touche, dans les douze variantes d’environnement embarquées, soit toute la surface distribuée par Next.js. Les informations publiques ne permettent pas de vérifier si le signalement initial utilisait exactement l’un de ces chemins ou une combinaison : Vercel n’a publié aucun détail d’exploit. La remédiation couvre cette classe dans les deux cas, car chaque chemin vers le désérialiseur passe désormais par les vérifications ajoutées.
Comment vérifier si j’ai été compromis ?
Aucun indicateur de compromission n’a été publié pour ce bulletin. En l’absence de recommandations publiques, recherchez ces signaux généraux :
- Du trafic POST inattendu vers les routes sous Cache Components, notamment des requêtes avec un corps form-data dont les noms de champs portent des préfixes de sérialisation plutôt que des champs de formulaire ordinaires.
- Des entrées du gestionnaire de cache dont les clés ou les payloads semblent malformés ou contiennent des identifiants de blocs qui ne correspondent pas à des écritures légitimes, si ce gestionnaire journalise les clés.
- Des traces de pile côté serveur provenant du désérialiseur flight, telles que des erreurs de résolution de références serveur ou d’initialisation de blocs de modèle, qui ne correspondent pas à votre code.
- Toute preuve d’exécution de code au moment de ces erreurs : nouveaux fichiers, connexions sortantes du processus serveur ou modification de l’état de l’environnement.
Si vous repérez une activité suspecte, considérez le processus serveur comme entièrement compromis : renouvelez tous les secrets qu’il pouvait lire, notamment les jetons de déploiement, les identifiants de bases de données et les clés d’API, puis reconstruisez le déploiement depuis un état réputé sain.
Chronologie
- 14 octobre 2026 : Vercel publie GHSA-673q-mjm6-r5xr et Next.js 16.4.1, qui contient le correctif du transport Server Component (commit a4cea6a, rédigé le 14 octobre 2026).
- 22 octobre 2026 : le bulletin est publié et examiné dans la GitHub Advisory Database.
- 23 octobre 2026 : publication de cet article. Aucune preuve de concept n’est publique, aucune exploitation réelle n’est connue et la faille ne figure pas dans le catalogue CISA KEV.
FAQ
Existe-t-il une CVE pour ce problème ?
Au 23 octobre 2026, le bulletin GHSA-673q-mjm6-r5xr n’a pas d’identifiant CVE ; il est suivi sous son identifiant GHSA dans la GitHub Advisory Database.
Cache Components doit-il être activé pour que je sois concerné ?
Le bulletin décrit la vulnérabilité dans les applications utilisant App Router avec Cache Components et une fonction use cache renvoyant plusieurs tableaux typés. Toutefois, Vercel demande à tous les utilisateurs d’effectuer immédiatement la mise à jour, même sans ces fonctions, et 16.4.1 corrige aussi la RCE Image Optimization. En pratique, il faut donc mettre à jour dans tous les cas.
Que sont les tableaux typés, et pourquoi leur multiplicité compte-t-elle ?
Un tableau typé, comme Uint8Array, est une vue binaire de longueur fixe sur un bloc de mémoire. Dans le protocole flight, chacun est sérialisé comme son propre bloc avec sa propre clé. Une fonction qui en renvoie plusieurs produit donc un payload contenant de nombreuses entrées de blocs, ce qui correspond à la forme décrite par la condition de déclenchement publiée.
Puis-je simplement retirer les tableaux typés de mes fonctions mises en cache ?
Cela supprime de votre code la condition de déclenchement publiée, mais ne constitue pas une correction : Vercel indique qu’aucune solution de contournement n’existe et demande une mise à jour immédiate. Une modification de code peut aussi réintroduire la condition dans votre application. Mettez à jour vers 16.4.1.
Qui a découvert et corrigé cette faille ?
L’utilisateur GitHub evolutionstorm a signalé le problème et est crédité comme découvreur dans l’entrée de la GitHub Advisory Database. Le correctif a été rédigé par des membres de Vercel dans l’environnement React Server Components embarqué, et le bulletin a été publié par l’équipe sécurité de Vercel.
Quelle est la popularité du logiciel affecté ?
Le package next a enregistré 44 586 009 téléchargements sur npm pendant la semaine du 16-22 octobre 2026, ce qui en fait l’un des frameworks web les plus déployés de l’écosystème JavaScript.
Scannez vos applications Next.js avec Ostorlab pour repérer les versions 16.x affectées et l’utilisation de use cache dans vos dépôts.
Références
- Bulletin Vercel GHSA-673q-mjm6-r5xr : https://github.com/vercel/next.js/security/advisories/GHSA-673q-mjm6-r5xr (consulté le 23 octobre 2026)
- Bulletin React GHSA-xqc2-xf4c-x3h3, la même RCE liée aux tableaux typés dans react-server-dom-webpack, react-server-dom-turbopack et react-server-dom-parcel, corrigée dans 19.0.9, 19.1.10, 19.2.9 et 19.3.1 : https://github.com/react/react/security/advisories/GHSA-xqc2-xf4c-x3h3 (consulté le 26 octobre 2026)
- Entrée de la GitHub Advisory Database (sévérité, CVSS v4.0 9.5 et CWE-502 confirmés le 24 octobre 2026) : https://github.com/advisories/GHSA-673q-mjm6-r5xr
- Commit de correction a4cea6a, « Improve Server Component transport protocol » : https://github.com/vercel/next.js/commit/a4cea6aec5da89d116b8960e113189cf51745d2f (consulté le 23 octobre 2026)
- Notes de version Next.js 16.4.1 : https://github.com/vercel/next.js/releases/tag/v16.4.1 (consultées le 23 octobre 2026)
- Nombre de téléchargements npm de
next(44 586 009 téléchargements, semaine du 16-22 octobre 2026) : https://api.npmjs.org/downloads/point/last-week/next (consulté le 24 octobre 2026)