Brotli CVE-2026-111497: aggiornare Node.js, Bun e Python
CVE-2026-111497 è un bug di corruzione della memoria nel decoder Brotli di Google, il codice C che decomprime i dati Content-Encoding: br. Dati compressi costruiti ad arte scrivono oltre la fine di un buffer, e questo può mandare in crash il programma, far trapelare memoria o eseguire il codice dell'attaccante. Node.js, Bun, i pacchetti Python Brotli e brotlicffi ed Envoy includono ciascuno la propria copia del decoder, quindi ognuno va aggiornato separatamente. Questa guida mostra quando un'applicazione è davvero esposta, come controllare un server con un solo comando e come distribuire la correzione senza dimenticare alcun container.
Quanto è grave
- Google le assegna 9.2 (critical) su CVSS 4.0, Node.js la classifica high, Red Hat 8.1.
- I ricercatori di Imperva hanno eseguito il proprio codice su un Node.js 24.22.0 non aggiornato su Linux ARM64, sia tramite una risposta ottenuta con fetch sia tramite il corpo di una richiesta Express. L'exploit non è pubblico.
- La valutazione della CISA del 23 ottobre non ha registrato sfruttamenti reali, e non esiste un proof of concept pubblico affidabile.
- La falla funziona in entrambe le direzioni: un server che decomprime input compressi può essere attaccato da un client, e un client che decomprime risposte compresse può essere attaccato da un server.
La correzione è entrata nel codice sorgente di Brotli il 27 febbraio, ma nessuna release di Brotli l'ha inclusa fino al 21 ottobre: per questo tutti i runtime corretti sono usciti nella stessa settimana.
Sei esposto?
È a rischio solo il codice che decodifica dati Brotli controllati da qualcun altro:
- Node.js e Bun - il
fetch()integrato quando legge una risposta inviata conContent-Encoding: br, le chiamate Brotli dinode:zlibsu dati non attendibili e i parser del corpo che decomprimono upload in Brotli (Express 5, body-parser 2). Un semplice server HTTP Node.js non decodifica da solo il corpo delle richieste. - Python - basta che sia installato il pacchetto Brotli o brotlicffi: requests, urllib3, httpx e aiohttp decodificano allora da soli le risposte Brotli, anche se il tuo codice non importa mai Brotli.
- Envoy e Istio - solo se è configurato un filtro di decompressione Brotli.
Qualsiasi servizio che chiama API di terze parti, webhook, provider di pagamento o fa scraping di pagine esegue fetch() verso server che non controlli. Togliere br da Accept-Encoding non è una correzione: un server può inviare Brotli comunque.
| Software | Vulnerabile | Corretto in |
|---|---|---|
| Node.js | 22.x, 24.x, 26.x prima della correzione; 20.x e precedenti (fine supporto) | 22.23.4, 24.22.1, 26.12.1 |
| Bun | 1.4.3 e precedenti | 1.4.4 |
| Python Brotli | 1.2.0 e precedenti | 1.2.1 |
| Python brotlicffi | 1.2.0.x e precedenti | 1.2.1.0 |
| Google Brotli (libreria C) | 1.0.3 - 1.2.0 | 1.0.10, 1.1.1, 1.2.1 |
| Envoy | 1.39.2, 1.38.5, 1.37.7, 1.36.11 | 1.40.0, 1.39.3, 1.38.6, 1.37.8, 1.36.12 |
| Istio | 1.31.0 - 1.31.1, 1.30.0 - 1.30.5, 1.29.0 - 1.29.8 | 1.31.2, 1.30.6, 1.29.9 |
Controllare un server con un comando
Questo script shell POSIX esamina Bun, Node.js e Python sulla macchina (o dentro un container) e indica quali sono più vecchi della correzione. Salvalo come brotli-check.sh:
#!/bin/sh
# CVE-2026-111497 (Brotli decoder): which runtimes on this host are older than the fixes?
ver_lt() { [ "$1" = "$2" ] && return 1; [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]; }
if command -v bun >/dev/null 2>&1; then
v=$(bun --version)
if ver_lt "$v" 1.4.4; then echo "bun $v: VULNERABLE, upgrade to 1.4.4+"; else echo "bun $v: fixed"; fi
fi
if command -v node >/dev/null 2>&1; then
v=$(node -p "process.versions.bun ? '' : process.versions.node")
if [ -z "$v" ]; then echo "node: this is Bun's node shim, see the bun line"; else major=${v%%.*}
case $major in
22) fix=22.23.4 ;; 24) fix=24.22.1 ;; 26) fix=26.12.1 ;;
*) fix= ;;
esac
if [ -z "$fix" ]; then echo "node $v: no fixed release for this line, move to 22, 24 or 26"
elif ver_lt "$v" "$fix"; then echo "node $v: VULNERABLE, upgrade to $fix+"
else echo "node $v: fixed"; fi
fi
fi
for py in python3 python; do
command -v $py >/dev/null 2>&1 || continue
$py - <<'PY'
from importlib import metadata
for pkg, fix in (("Brotli", "1.2.1"), ("brotlicffi", "1.2.1.0")):
try:
v = metadata.version(pkg)
except metadata.PackageNotFoundError:
print(f"python {pkg}: not installed")
continue
a = [int(x) for x in v.split(".") if x.isdigit()]
b = [int(x) for x in fix.split(".")]
print(f"python {pkg} {v}: {'VULNERABLE, upgrade to ' + fix + '+' if a < b else 'fixed'}")
PY
break
done
sh brotli-check.sh
docker exec my-app sh -s < brotli-check.sh # the same check inside a running container
Output verificato il 2026-10-24 (un portatile di sviluppo, poi le immagini Docker ufficiali):
bun 1.4.1: VULNERABLE, upgrade to 1.4.4+
node 26.6.0: VULNERABLE, upgrade to 26.12.1+
python Brotli: not installed
python brotlicffi: not installed
bun 1.4.4: fixed
node: this is Bun's node shim, see the bun line
node 22.23.4: fixed
node 16.20.2: no fixed release for this line, move to 22, 24 or 26
python Brotli 1.1.0: VULNERABLE, upgrade to 1.2.1+
python brotlicffi: not installed
Eseguilo in ogni container, non solo sull'host: il Node.js dell'host non dice nulla di quello incluso in un'immagine.
Come distribuire la correzione
- Trova ogni copia. Le immagini sono la dimenticanza più comune. Elenca cosa è in esecuzione:
docker ps --format '{{.Image}}', poi le righeFROMdei tuoi Dockerfile. Un tag comenode:22ooven/bun:alpineavanza solo quando fai pull; un tag fissato comeoven/bun:1.4.1-slimnon avanza mai. - Aggiorna il runtime. Alza i tag fissati (
oven/bun:1.4.4-slim,node:22.23.4-alpine), eseguibun upgradee aggiorna i pacchetti Node.js sugli host, epip install -U "Brotli>=1.2.1"(obrotlicffi>=1.2.1.0) negli ambienti Python. Se un'applicazione Python non ha bisogno di Brotli, anchepip uninstall Brotli brotlicffichiude la strada: i client HTTP ripiegano su gzip e deflate. - Ricostruisci e sostituisci.
docker compose build --pull && docker compose up -d. Un processo in esecuzione mantiene il vecchio decoder finché non viene riavviato, quindi riavvia anche i servizi sugli host. - Verifica. Esegui di nuovo lo script di controllo in ogni container (
docker exec <name> bun --versionè la versione rapida). I lockfile con--frozen-lockfilepossono far fallire la ricostruzione dopo l'aggiornamento delle dipendenze: fai commit del lockfile aggiornato insieme apackage.json. - Non puoi ancora aggiornare? Rifiuta sul proxy le richieste con
Content-Encoding: brprima che qualcosa decodifichi il corpo, ed evita di scaricare dati da host non attendibili. Vercel fa notare che le regole del firewall non coprono le risposte che il tuo stesso codice scarica da altri server - lì serve solo l'aggiornamento.
Per uno stack Node.js su una VM vedi Server cloud per Node.js, per Python Server cloud per Python e Flask e per immagini che si ricostruiscono senza intoppi Server cloud per Docker Swarm.
Cosa abbiamo fatto
Nel giro di pochi giorni dalle correzioni abbiamo portato a 1.4.4 ogni servizio DCXV che gira su Bun: le immagini base fissate sono passate a tag corretti e tutte le immagini sono state ricostruite con un pull nuovo. I nostri componenti Node.js non decodificano corpi Brotli e i nostri proxy non hanno decompressione Brotli. I server dei clienti sono gestiti dai loro proprietari: se usi Node.js, Bun o Python su un server cloud DCXV, esegui oggi stesso il controllo qui sopra, oppure chiedici dell'amministrazione gestita dei server.
Fonti (verificate il 2026-10-24): le release di sicurezza di Node.js di ottobre 2026 (22.23.4, 24.22.1, 26.12.1), la release Bun 1.4.4, la valutazione della CISA del 23 ottobre 2026 e la ricerca di Imperva.
