Brotli CVE-2026-111497: łatka dla Node.js, Bun i Pythona

Brotli CVE-2026-111497: łatka dla Node.js, Bun i Pythona

Brotli CVE-2026-111497: łatka dla Node.js, Bun i Pythona

CVE-2026-111497 to błąd uszkodzenia pamięci w dekoderze Brotli od Google, czyli kodzie w C, który rozpakowuje dane Content-Encoding: br. Spreparowane skompresowane dane zapisują poza końcem bufora, co może wywrócić program, ujawnić zawartość pamięci albo uruchomić kod atakującego. Node.js, Bun, pakiety Pythona Brotli i brotlicffi oraz Envoy mają każdy własną kopię tego dekodera, więc każdy trzeba zaktualizować osobno. Ten poradnik pokazuje, kiedy aplikacja jest naprawdę narażona, jak sprawdzić serwer jednym poleceniem i jak wdrożyć poprawkę, nie pomijając żadnego kontenera.

Jak poważna jest luka

  • Google ocenia ją na 9.2 (critical) w CVSS 4.0, Node.js jako high, Red Hat na 8.1.
  • Badacze z Imperva uruchomili własny kod na niezałatanym Node.js 24.22.0 na Linux ARM64, zarówno przez odpowiedź pobraną przez fetch, jak i przez treść żądania w Express. Exploit nie jest publiczny.
  • Ocena CISA z 23 października nie odnotowała wykorzystania luki w realnych atakach i nie ma wiarygodnego publicznego proof of concept.
  • Luka działa w obie strony: serwer, który rozpakowuje skompresowane dane wejściowe, może zostać zaatakowany przez klienta, a klient, który rozpakowuje skompresowane odpowiedzi, przez serwer.

Poprawka trafiła do kodu źródłowego Brotli 27 lutego, ale żadne wydanie Brotli nie zawierało jej aż do 21 października, dlatego wszystkie załatane runtime'y pojawiły się w tym samym tygodniu.

Czy jesteś narażony?

Zagrożony jest tylko kod, który dekoduje dane Brotli kontrolowane przez kogoś innego:

  • Node.js i Bun - wbudowany fetch() czytający odpowiedź wysłaną z Content-Encoding: br, wywołania Brotli z node:zlib na niezaufanych danych oraz parsery treści żądań, które rozpakowują przesyłane pliki w Brotli (Express 5, body-parser 2). Zwykły serwer HTTP w Node.js sam nie dekoduje treści żądań.
  • Python - wystarczy, że zainstalowany jest pakiet Brotli lub brotlicffi: requests, urllib3, httpx i aiohttp same dekodują wtedy odpowiedzi Brotli, nawet jeśli Twój kod nigdy nie importuje Brotli.
  • Envoy i Istio - tylko jeśli skonfigurowano filtr dekompresji Brotli.

Każda usługa, która wywołuje zewnętrzne API, webhooki, operatorów płatności albo scrapuje strony, wykonuje fetch() do serwerów, których nie kontrolujesz. Usunięcie br z Accept-Encoding nie jest poprawką: serwer i tak może wysłać Brotli.

Oprogramowanie Podatne Poprawione w
Node.js 22.x, 24.x, 26.x przed poprawką; 20.x i starsze (koniec wsparcia) 22.23.4, 24.22.1, 26.12.1
Bun 1.4.3 i starsze 1.4.4
Python Brotli 1.2.0 i starsze 1.2.1
Python brotlicffi 1.2.0.x i starsze 1.2.1.0
Google Brotli (biblioteka 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

Sprawdź serwer jednym poleceniem

Ten skrypt powłoki POSIX sprawdza Bun, Node.js i Pythona na maszynie (lub w kontenerze) i podaje, które są starsze niż poprawka. Zapisz go jako 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

Wynik sprawdzony 2026-10-24 (laptop programisty, potem oficjalne obrazy Dockera):

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

Uruchom go w każdym kontenerze, nie tylko na hoście: Node.js na hoście nic nie mówi o tym, który jest wbudowany w obraz.

Jak wdrożyć poprawkę

  1. Znajdź każdą kopię. Najczęściej pomijane są obrazy. Wypisz, co działa: docker ps --format '{{.Image}}', potem linie FROM w Twoich Dockerfile. Tag taki jak node:22 czy oven/bun:alpine przesuwa się do przodu tylko wtedy, gdy zrobisz pull; przypięty tag, np. oven/bun:1.4.1-slim, nie przesuwa się nigdy.
  2. Zaktualizuj runtime. Podbij przypięte tagi (oven/bun:1.4.4-slim, node:22.23.4-alpine), na hostach wykonaj bun upgrade i zaktualizuj pakiety Node.js, a w środowiskach Pythona pip install -U "Brotli>=1.2.1" (lub brotlicffi>=1.2.1.0). Jeśli aplikacja w Pythonie nie potrzebuje Brotli, pip uninstall Brotli brotlicffi również zamyka tę drogę: klienty HTTP przechodzą wtedy na gzip i deflate.
  3. Przebuduj i podmień. docker compose build --pull && docker compose up -d. Działający proces trzyma stary dekoder aż do restartu, więc zrestartuj też usługi na hostach.
  4. Zweryfikuj. Uruchom skrypt ponownie w każdym kontenerze (docker exec <name> bun --version to szybka wersja). Lockfile z --frozen-lockfile mogą wywrócić przebudowę po podbiciu zależności: commituj zaktualizowany lockfile razem z package.json.
  5. Nie możesz jeszcze zaktualizować? Odrzucaj na proxy żądania z Content-Encoding: br, zanim cokolwiek zdekoduje treść, i unikaj pobierania danych z niezaufanych hostów. Vercel zaznacza, że reguły firewalla nie obejmują odpowiedzi, które Twój własny kod pobiera z innych serwerów - pomaga tylko aktualizacja.

Dla stosu Node.js na VM zobacz Serwer cloud dla Node.js, dla Pythona Serwer cloud dla Pythona i Flaska, a dla obrazów, które czysto się przebudowują, Serwer cloud dla Docker Swarm.

Co zrobiliśmy

W ciągu kilku dni od wydania poprawek przenieśliśmy każdą usługę DCXV działającą na Bun na 1.4.4: przypięte obrazy bazowe dostały poprawione tagi, a wszystkie obrazy zostały przebudowane ze świeżym pull. Nasze komponenty Node.js nie dekodują treści Brotli, a nasze proxy nie mają dekompresji Brotli. Serwerami klientów zarządzają ich właściciele: jeśli uruchamiasz Node.js, Bun lub Pythona na serwerze cloud DCXV, wykonaj powyższe sprawdzenie jeszcze dziś albo zapytaj nas o zarządzaną administrację serwerem.

Źródła (sprawdzone 2026-10-24): październikowe wydania bezpieczeństwa Node.js 2026 (22.23.4, 24.22.1, 26.12.1), wydanie Bun 1.4.4, ocena CISA z 23 października 2026 i badania Imperva.

Starlink Mini 2 za CGNAT: jak dotrzeć do miejsca, które się przenosi
networkingcloudinfrastructure

Starlink Mini 2 za CGNAT: jak dotrzeć do miejsca, które się przenosi

Starlink Mini 2 już jest, ale standardowe plany stoją za CGNAT. Policzyliśmy 990 208 IPv4 na 12+ mln klientów i porównaliśmy cztery drogi do zdalnego miejsca.

Transfery IPv4 w LACNIC w 2026: zasady, opłaty, rok blokady
ipv4transfernetworking

Transfery IPv4 w LACNIC w 2026: zasady, opłaty, rok blokady

Transfery IPv4 w LACNIC w 2026: zasady sekcji 2.3.2.18, opłata US$ 1,037, NIC.br i NIC México oraz nasza analiza 1,929 transferów z lat 2016-2026.

Protesty przeciw centrom danych w Europie: co robić
clouddedicatedinfrastructure

Protesty przeciw centrom danych w Europie: co robić

6 listopada setki osób weszły na budowę centrum danych Microsoft w Amsterdamie. Liczby holenderskiej sieci, zmierzone opóźnienia do hubów UE i co robić.

Centra danych przegrały 45 do 1 w USA: wnioski dla klientów
clouddedicatedinfrastructure

Centra danych przegrały 45 do 1 w USA: wnioski dla klientów

47 lokalnych głosowań w USA o centrach danych, strona restrykcyjna wygrała w 45. Przeliczamy surowe dane i wyjaśniamy, co to oznacza dla najemców serwerów.

AWS BYOIP w 2026: $43.80 za IPv4 czy własna pula /24
ipv4pricingcloud

AWS BYOIP w 2026: $43.80 za IPv4 czy własna pula /24

AWS pobiera $0.005 za godzinę za publiczny IPv4, $43.80 rocznie. BYOIP jest wolny od tej opłaty: kiedy kupiony lub dzierżawiony /24 się zwraca, z kontrolą ROA.