C’è un modo molto efficiente per sentirsi tranquilli senza aver risolto niente: aprire il progetto, eseguire npm audit, vedere che non ci sono vulnerabilità note nelle dipendenze dichiarate e concludere che il backend è aggiornato. Il problema è che l’applicazione non gira dentro package.json. Gira dentro un runtime, con librerie native, parser, certificati e regole di validazione che il lockfile del progetto non descrive.
Il 21 ottobre 2026 Node.js ha pubblicato un bollettino di sicurezza per tre linee: 26.x, 24.x e 22.x. Le versioni correttive sono rispettivamente 26.12.1 (Current), 24.22.1 e 22.23.4 (LTS). Non è una notizia interessante perché contiene tre numeri di versione. È interessante perché mostra quanto codice critico attraversi una normale richiesta HTTP prima ancora di raggiungere la logica che abbiamo scritto.
Un aggiornamento, tre rami, superfici di rischio diverse
L’avviso ufficiale elenca dieci CVE: quattro di gravità High, tre Medium e tre Low. Non tutte interessano gli stessi rami. È una distinzione che conta: un inventario che registra genericamente “usiamo Node” non consente di stabilire quali processi vadano aggiornati e con quale urgenza. E neppure l’etichetta LTS è una scorciatoia: significa manutenzione prevista, non immunità dalle vulnerabilità.
Fra i problemi High, CVE-2026-111497 riguarda una scrittura fuori dai limiti nel decoder Brotli, che il progetto descrive come potenzialmente capace di consentire esecuzione remota di codice. CVE-2026-105482 riguarda la gestione di nomi DNS contenenti un byte NUL. CVE-2026-106069 riguarda la verifica TLS di host interpretati in modo troppo permissivo come indirizzi IP. Questi tre problemi interessano tutti e tre i rami. CVE-2026-105483, invece, è un bypass della verifica hostname TLS con cifre fullwidth e interessa 26.x e 24.x, non 22.x.
Non significa che ogni applicazione Node sia automaticamente sfruttabile allo stesso modo. Per valutare l’esposizione reale bisogna considerare input non fidati, API utilizzate, configurazione e percorso dei dati. Ma l’assenza di un exploit riprodotto nel proprio ambiente non trasforma un runtime vulnerabile in un runtime corretto.
Il codice che non abbiamo scritto può decidere la sicurezza del servizio
Brotli: il confine fra JavaScript e codice nativo
Quando un servizio comprime o decomprime contenuti, il lavoro non è necessariamente svolto da qualche innocuo frammento JavaScript. Il decoder Brotli è un esempio concreto di codice nativo e dipendenze incorporate nel runtime. Se il problema è lì, aggiornare una libreria applicativa non sostituisce la patch della versione Node distribuita. Non basta neppure guardare la dipendenza diretta nel progetto: il difetto può trovarsi diversi strati sotto.
Questo è il punto meno spettacolare e più utile dell’aggiornamento: il perimetro del software è più grande del repository. Chi gestisce un backend deve conoscere non soltanto cosa ha installato con npm, ma anche cosa è stato compilato, impacchettato o ereditato dalla base del container.
TLS e DNS: due interpretazioni diverse della stessa destinazione
Le correzioni su TLS e DNS raccontano un problema architetturale ricorrente. Un componente valida un hostname, un altro lo normalizza, un terzo lo risolve. Se i tre passaggi non attribuiscono lo stesso significato a un input, il confine di fiducia può spostarsi senza che il codice applicativo se ne accorga. Un byte NUL, una rappresentazione insolita di un indirizzo IP o una forma non canonica possono trasformare un controllo apparentemente ragionevole in un controllo insufficiente.
La lezione non è inventare un parser di indirizzi migliore di quello del runtime. È ridurre le assunzioni implicite: definire dove si validano gli input, quale rappresentazione è canonica, quali destinazioni sono ammesse e chi decide se un certificato corrisponde davvero all’endpoint richiesto. Quando un sistema attraversa più livelli, la coerenza fra quei livelli è una proprietà di sicurezza, non un dettaglio estetico.
Un progetto può avere dipendenze applicative perfettamente aggiornate e continuare a eseguire codice vulnerabile. La sicurezza riguarda l’intero sistema distribuito, non soltanto l’elenco dei pacchetti.
La catena di fiducia cambia anche senza toccare il nostro codice
Nel ciclo del 21 ottobre sono presenti aggiornamenti di componenti incorporati come c-ares e Undici per tutti e tre i rami; OpenSSL per 24.x e 22.x; SQLite per 22.x. Il bollettino segnala inoltre aggiornamenti ai certificati legati alla compromissione delle CA intermedie eMudhra. Le note della 26.12.1 riportano anche il rifiuto di catene contenenti chiavi pubbliche bloccate.
Non è una questione separata dal funzionamento delle API. Un processo Node può stabilire connessioni TLS senza passare dal browser dell’utente; la fiducia nei certificati viene valutata nel contesto del runtime e della sua configurazione. È lo stesso motivo per cui, parlando della vicenda eMudhra, ho sottolineato che aggiornare il browser non basta. Un servizio backend non eredita automaticamente ogni decisione di sicurezza presa dal browser sul desktop.
L’aggiornamento del trust store può anche rendere visibili problemi che prima restavano nascosti: connessioni verso endpoint con catene non più considerate affidabili, certificati intermedi da sostituire, ambienti di test rimasti indietro. È un motivo in più per testare le integrazioni, non per rinviare indefinitamente la patch.
Il problema operativo è sapere quale Node gira davvero
La domanda utile non è “abbiamo aggiornato Node?”. È “quale binario Node sta eseguendo ciascun servizio, in quale ambiente e con quale immagine o processo di deploy?”. Una macchina di sviluppo aggiornata non dice nulla su un container in produzione. Il valore dichiarato in engines non prova la versione realmente avviata. Una pipeline che ricostruisce l’applicazione senza aggiornare la base image può distribuire ancora lo stesso runtime vulnerabile.
Per affrontare il problema in modo verificabile, la sequenza minima è questa:
- Inventario: rilevare la versione effettiva con
node --versionnei processi e negli ambienti interessati, inclusi container, worker, job e istanze di staging. - Classificazione: associare ogni servizio al ramo 22.x, 24.x o 26.x e alla relativa patch, verificando anche eventuali versioni fuori supporto.
- Ricostruzione: aggiornare il runtime o la base image e rigenerare gli artefatti distribuiti; non limitarsi a modificare
package-lock.json. - Verifica: testare connessioni TLS, DNS, decompressione, richieste HTTP e integrazioni critiche; controllare la versione realmente attiva dopo il rollout.
- Tracciabilità: registrare quale build è stata distribuita, quando, dove e con quale possibilità di rollback.
Sono attività poco appariscenti. Non fanno una bella demo. Però distinguono un aggiornamento eseguito da un aggiornamento dimostrabile. E aiutano a capire perché il lockfile non è una garanzia assoluta di integrità: blocca una parte importante della risoluzione delle dipendenze, non fotografa da solo l’intero ambiente esecutivo.
Patching non significa soltanto sostituire un numero
La release 26.x è Current; le 24.x e 22.x sono LTS. Scegliere una linea supportata e installare la patch pertinente è il primo passo, non la fine del lavoro. Un aggiornamento che cambia il comportamento di validazione TLS può far fallire una connessione che prima funzionava. Non è necessariamente una regressione da annullare: potrebbe essere proprio la dimostrazione che un controllo troppo permissivo è stato corretto. La risposta sensata è capire quale integrazione non soddisfa più il contratto di sicurezza.
Allo stesso tempo, il rollout richiede metodo. Ambienti rappresentativi, test di regressione, osservabilità degli errori TLS/DNS, rollout progressivo dove possibile e rollback pianificato sono strumenti per applicare una correzione senza trasformarla in un’interruzione non gestita. Se una linea è fuori supporto, non basta cercare il pacchetto più recente disponibile in quel ramo: va pianificata una migrazione a una linea mantenuta.
Il bollettino del 21 ottobre non dimostra che il software moderno sia inevitabilmente insicuro. Dimostra una cosa più concreta: la manutenzione della piattaforma è parte della progettazione. Se l’architettura non permette di sapere quale runtime è in esecuzione, aggiornare rapidamente, testare i confini e verificare il risultato, allora il problema non nasce il giorno in cui esce la CVE. Quel giorno diventa soltanto visibile.
Il prossimo controllo utile è semplice: confrontare l’inventario delle versioni Node effettivamente in esecuzione con 26.12.1, 24.22.1 e 22.23.4. Se quell’inventario non esiste, prima della patch manca già un pezzo di sistema.
Domande frequenti
Quali versioni sono state aggiornate?
Node.js 26.12.1, 24.22.1 e 22.23.4.
Basta aggiornare npm?
No, bisogna aggiornare anche il runtime Node.js.
Come controllo il runtime?
Verifica la versione effettivamente in esecuzione dopo il deploy.
