La patch corregge il codice, non la topologia
Il 3 novembre 2026 Next.js ha pubblicato un aggiornamento di sicurezza per le linee 16.4.5 e 15.5.29. Non è la solita notizia da trasformare in un elenco di sigle: nel bollettino ufficiale compaiono problemi che diventano sfruttabili solo quando più componenti si comportano in un certo modo. Cache condivise, parametri di routing, risposte HTTP compresse, cookie e ottimizzazione delle immagini. Il framework è una parte del sistema, non tutto il sistema.
Per un’applicazione ospitata autonomamente la domanda utile non è soltanto quale versione di Next.js sia installata. È anche quali header attraversino il reverse proxy, dove siano condivise le cache, chi possa controllare una risposta upstream e quali confini separino utenti e tenant. La versione corretta è necessaria; conoscere il percorso effettivo di una richiesta è ciò che permette di verificare se la correzione copre il deployment reale.
Il reverse proxy non è una scatola trasparente
Una delle vulnerabilità descritte da Next.js riguarda applicazioni self-hosted che restituiscono senza modificarla una risposta ottenuta con fetch da un upstream controllabile dall’attaccante. In determinate condizioni il corpo viene decompresso, mentre gli header di framing possono continuare a descrivere la risposta compressa. Il problema diventa significativo dietro un reverse proxy HTTP/1.1 che accoda risposte pipelined su una connessione backend condivisa: un disallineamento può associare una risposta al client sbagliato.
Non significa che qualunque installazione dietro nginx sia vulnerabile, né che basti usare HTTP/1.1 per riprodurre l’attacco. Servono il percorso applicativo interessato, il controllo della risposta upstream e il comportamento specifico del proxy. La distinzione è fondamentale: una checklist che dice “controlla il proxy” è utile, una che dichiara compromessi tutti i proxy è disinformazione. L’advisory GHSA-pc58-hwjq-p385 delimita esplicitamente queste precondizioni.
La cache diventa un confine di sicurezza
Altre tre correzioni riguardano l’isolamento delle cache. Con Cache Components abilitato, la combinazione di unstable_cache e use cache può, in una specifica sequenza concorrente di riempimento, far riutilizzare un risultato legato a un parametro root differente. Se quel parametro rappresenta un tenant, il contenuto può passare da un contesto all’altro. Una seconda variante coinvolge funzioni use cache annidate e valori asincroni non attesi; una terza riguarda argomenti binari distinti che possono produrre la stessa identità di cache.
Il punto architetturale è che una cache non è soltanto una scorciatoia per risparmiare millisecondi. Se memorizza dati dipendenti da utente, account o tenant, la chiave e le dipendenze diventano parte dell’autorizzazione effettiva. Una query può essere perfettamente protetta a monte e perdere l’isolamento quando il risultato viene riutilizzato in un contesto diverso. Questo non rende ogni cache insicura: rende obbligatorio verificare esattamente quali informazioni determinino la sua identità.
Nella pratica conviene testare richieste concorrenti di tenant diversi, cache fredda e cache calda, valori di routing generati e non generati al build. Non basta controllare il risultato della prima richiesta: alcune condizioni si manifestano solo quando la seconda eredita un elemento memorizzato dalla prima.
Cookie HttpOnly: il dettaglio è l’header
Il bollettino segnala anche una perdita di valori di cookie in specifiche route Edge. Quando una route interessata usa NextResponse.cookies.set(), il valore serializzato può comparire nell’header interno x-middleware-set-cookie. A differenza di Set-Cookie, tale header può essere leggibile da JavaScript same-origin, anche se il cookie è marcato HttpOnly.
L’attacco richiede però che sullo stesso origin venga eseguito JavaScript controllato dall’attaccante e che la risposta contenga un cookie sensibile. Non è una vulnerabilità che da sola esegue JavaScript. Next.js specifica inoltre che il comportamento riprodotto non riguarda i Route Handler Node.js o le route che impostano i cookie tramite cookies().set(). Questa precisione conta più di qualunque titolo allarmistico.
Un SVG che sembra AVIF non è un AVIF
L’ottimizzatore immagini introduce un altro confine: il filtro configurato con images.dangerouslyAllowSVG: false può essere aggirato da un’immagine proveniente da una sorgente consentita ma classificata erroneamente come AVIF. Un backend nativo capace di decodificare SVG può poi interpretare i byte originali come SVG. L’allowlist delle sorgenti rimane applicata: non è un bypass generale di ogni restrizione.
La conseguenza è una maggiore esposizione al parser SVG e alle sue eventuali vulnerabilità o a consumi eccessivi di risorse. Il bollettino non dimostra che questo specifico difetto produca automaticamente XSS o esecuzione remota di codice. È importante non trasformare un percorso di attacco possibile in un impatto già dimostrato.
Cosa verificare prima di dichiarare chiuso l’incidente
Il primo intervento è aggiornare le dipendenze alla release corretta della propria linea: next@16.4.5 oppure next@15.5.29, secondo le indicazioni pubblicate da Next.js. Poi bisogna verificare la versione effettivamente eseguita nei container e nei processi, non soltanto quella presente nel repository. Un’immagine Docker non ricostruita può continuare a servire codice vecchio dopo una modifica del package manifest.
Il secondo intervento è mappare i percorsi esposti: Route Handler che inoltrano risposte esterne, proxy che riutilizzano connessioni HTTP/1.1, cache che dipendono da parametri tenant, route Edge che emettono cookie sensibili, ottimizzatori che accettano immagini da sorgenti esterne. Non tutte le applicazioni usano questi percorsi. Per quelle che li usano, il test deve attraversare la stessa catena che vede il traffico reale.
Il terzo intervento è osservare il comportamento. Nei test di staging, una risposta deve restare associata al suo client, un contenuto di tenant A non deve apparire a tenant B, un cookie sensibile non deve essere leggibile in header esposti al browser e un’immagine rifiutata dal criterio di sicurezza non deve raggiungere un decoder escluso dalla configurazione. Sono invarianti verificabili, non slogan.
La manutenzione finisce quando hai verificato il sistema
Il rilascio di novembre mostra un limite ricorrente delle applicazioni moderne: ogni componente può rispettare il proprio contratto locale mentre la composizione produce un comportamento inatteso. La cache decide di riutilizzare, il proxy decide come inquadrare una risposta, il decoder decide quale formato interpretare. Il confine di sicurezza attraversa tutte queste decisioni.
Aggiornare Next.js è il primo passo e va fatto senza aspettare. Ma la qualità della manutenzione si vede dopo il deploy, quando sai dimostrare che dati, identità e risposte non oltrepassano il confine sbagliato. È lo stesso principio che rende utile una verifica delle invarianti in un sistema distribuito: non basta che ogni pezzo funzioni; deve restare corretto il comportamento dell’insieme.
Domande frequenti
Domande frequenti
Quali versioni di Next.js correggono i problemi di novembre 2026?
Il bollettino ufficiale del 3 novembre indica Next.js 16.4.5 e 15.5.29. Va verificata anche la versione effettivamente eseguita dopo il deploy.
Tutte le applicazioni dietro un reverse proxy HTTP/1.1 sono vulnerabili?
No. Il problema di response framing richiede specifici Route Handler, una risposta upstream controllabile e un proxy che accoda risposte pipelined su una connessione backend condivisa.
Perché una cache può causare una fuga di dati tra tenant?
Se una chiave o le dipendenze della cache non distinguono correttamente i parametri che separano tenant o utenti, un risultato può essere riutilizzato in un contesto diverso. Le vulnerabilità pubblicate richiedono condizioni applicative precise.
Il bug dei cookie HttpOnly consente da solo di eseguire JavaScript?
No. La perdita attraverso l’header interno richiede JavaScript controllato dall’attaccante già in esecuzione sullo stesso origin e una route Edge interessata che imposti un cookie sensibile.
