Bezpieczeństwo Orientacyjny czas czytania: 11 min ·

Aktualizacja zabezpieczeń Node.js: czy Państwa wersja nadal dostaje poprawki?

21 października Node.js wydał poprawki zabezpieczeń dla release line 22, 24 i 26. Które wersje są bezpieczne, dlaczego Node.js 18 i 20 poprawek nie dostaną i jak sprawdzić, której wersji używa Państwa aplikacja.

Okno przeglądarki z kołem zębatym i znakiem potwierdzenia — aktualizacja zabezpieczeń Node.js

21 października Node.js wydał poprawki zabezpieczeń dla release line 22, 24 i 26. Które wersje są bezpieczne, dlaczego Node.js 18 i 20 poprawek nie dostaną i jak sprawdzić, której wersji używa Państwa aplikacja.

Tylko te release line, które nadal mają wsparcie, dostały poprawki, a bezpieczne są wersje 22.23.4, 24.22.1 i 26.12.1 albo nowsze w tej samej release line. Node.js 18 i 20 wsparcie już straciły, więc w tej aktualizacji zabezpieczeń Node.js poprawek dla nich nie ma i nie będzie: jeżeli Państwa aplikacja działa na którejś z nich, aktualizacja oznacza przejście na inną release line.

W środę, 21 października, projekt Node.js opublikował wydania zabezpieczeń dla wszystkich trzech wspieranych release line, a tego samego dnia PHP wydało awaryjne poprawki dla podatności, które dostały nazwę PHP2Shell, są to jednak dwa osobne zdarzenia o różnej wadze. Wydanie Node.js było zapowiedziane wcześniej, jego najpoważniejsze podatności projekt ocenił jako wysokie, a nie krytyczne, i w ogłoszeniu nie wymieniono ani tymczasowych rozwiązań, ani ataków w rzeczywistym środowisku. Aktualizacji to nie pozwala odłożyć, ale przesuwa uwagę: tym razem największe ryzyko nie leży w samym wydaniu, tylko w wersjach, które go nie dostały.

Co naprawiono w październikowej aktualizacji zabezpieczeń Node.js

W ogłoszeniu Node.js wymieniono dziesięć podatności: cztery projekt ocenił jako wysokie, trzy jako średnie i trzy jako niskie, ale ocen CVSS projekt tym razem nie opublikował, więc w tym artykule użyto tej samej oceny.

Najpoważniejsza z nich to CVE-2026-111497: projekt nazywa ją out-of-bounds write dekodera Brotli, zapisem poza przydzielonymi granicami pamięci, który pozwala na zdalne wykonanie kodu (RCE), i ocenia ją jako wysoką. Brotli to format kompresji, którego dekoder jest wbudowany w Node.js, a w notatkach wydania poprawka jest opisana jako zmiana w tej wbudowanej bibliotece. Którą drogą podatność jest osiągalna, w ogłoszeniu nie powiedziano, a opis poprawki autorów Brotli mówi tylko o tym, że nieprawidłowy strumień danych jest teraz rozpoznawany wcześniej, dlatego nie wymieniono tu konkretnych funkcji: pewne jest tylko to, że błąd dekodera najbardziej dotyczy aplikacji, które rozpakowują dane Brotli ze źródła, któremu nie mogą ufać.

Trzy inne podatności dotyczą sprawdzania certyfikatu TLS i nie są tym samym przypadkiem; pierwsza, z wysoką oceną, dotyczy tylko release line 26 i 24: hostname verification można obejść, jeżeli adres IP jest zapisany cyframi o pełnej szerokości, na przykład 127.0.0.1. Druga, też wysoka, dotyczy wszystkich trzech release line: sprawdzenie można obejść, jeżeli nazwę hosta da się odczytać nieściśle jako adres IP. Trzecia jest średnia i też dotyczy wszystkich trzech release line: niestandardowy zapis IPv4 pozwala nazwie DNS podanej w certyfikacie poświadczyć adres IP. W notatkach wydania poprawki są sformułowane krótko: nietypowo zapisane adresy IP są teraz odrzucane, a osobno naprawiono regresję normalizacji adresów IP. We wszystkich trzech przypadkach chodzi o moment, w którym Node.js działa jako klient, czyli sam nawiązuje szyfrowane połączenie z innym serwerem, a nie o moment, w którym obsługuje własnych odwiedzających.

Czwarta podatność o wysokiej ocenie jest w funkcji dns.resolve, która obcinała nazwy zawierające znak null; wśród średnich jest błąd ponownego użycia połączeń HTTP, w którym odpowiedź zapisana w połączeniu przed wysłaniem żądania mogła zostać potraktowana jako odpowiedź na to żądanie, oraz błąd, przez który pamięć podręczna losowości funkcji crypto.randomUUID() i randomInt() była zapisywana w startup snapshot i w jednym pliku wykonywalnym, tak że każdy proces powtarzał te same wartości, a trzy podatności o niskiej ocenie dotyczą obsługi plików ZIP i modelu uprawnień Node.js.

Błąd pamięci podręcznej losowości warto rozumieć dokładniej, bo w zwykłym przypadku nie ma on żadnego skutku: dotyczy tylko projektów, które startują ze startup snapshot przygotowanego we własnym zakresie albo są rozpowszechniane jako jeden plik wykonywalny (single executable application, SEA), i tam oznacza, że każdy proces uruchomiony z tego samego pliku dostawał te same „losowe” wartości. Jeżeli w takim projekcie za pomocą crypto.randomUUID() albo randomInt() tworzono identyfikatory lub kody tymczasowe, mogły się one powtarzać między procesami, więc po poprawce warto sprawdzić, czy któraś z tych wartości nie jest używana tam, gdzie ma być nie do odgadnięcia.

Wydanie aktualizuje też kilka wbudowanych zależności — zależnie od release line są to c-ares, OpenSSL, SQLite i Undici, na którym opiera się wbudowana funkcja fetch — a także CA store, czyli listę zaufanych urzędów certyfikacji, w związku ze skompromitowanym certyfikatem pośrednim eMudhra, i połączenia TLS odrzucają teraz łańcuchy, w których są zablokowane klucze publiczne.

Które wersje są bezpieczne

Poprawione wersje są trzy, po jednej dla każdej wspieranej release line: 22.23.4, 24.22.1 i 26.12.1, a wszystko, co w swojej release line jest starsze, jest dotknięte. Nie wszystkie dziesięć podatności dotyczy wszystkich release line: 26 ma wszystkie dziesięć, 24 osiem, a 22 siedem, bo dwa błędy o niskiej ocenie są tylko w najnowszej, a jeden błąd TLS nie dotyczy 22.

Aktualizacja w ramach tej samej release line to najmniejszy możliwy krok i zwykle też najbezpieczniejsza zmiana, jaką da się zrobić w środowisku produkcyjnym, mimo to ona też może zmienić zachowanie, i tym razem jest konkretny przykład: poprawka w sprawdzaniu TLS odrzuca teraz nietypowo zapisane adresy IP. Jeżeli jakaś aplikacja w połączeniach z innym serwerem używała kiedyś takiego zapisu, po aktualizacji połączenie może się nie udać: to naprawiona dziura w zabezpieczeniach, a nie nowy błąd, ale warto o tym wiedzieć przed aktualizacją, a nie po niej.

Release line jest ważniejsza niż numer wersji

Aż do Node.js 26 każda wersja główna przechodzi jeden cykl: najpierw jest wersją bieżącą (Current), potem wersje o numerze parzystym dostają aktywne wsparcie długoterminowe (Active LTS), następnie przechodzą do fazy Maintenance LTS, w której dostają tylko krytyczne poprawki błędów i poprawki zabezpieczeń, a na końcu wsparcie kończy się całkowicie, natomiast wersje o numerze nieparzystym aż do release line 25 wsparcia długoterminowego nie dostawały. Od Node.js 27 cykl się zmienia: według grafiku wydań każda release line dostanie wsparcie długoterminowe i przez wszystkie 30 miesięcy wsparcia pozostanie w fazie aktywnej, bez fazy Maintenance LTS. Projekt sam pisze, że aplikacje produkcyjne powinny używać tylko wersji ze wsparciem długoterminowym w fazie aktywnej albo w fazie Maintenance LTS.

Dla obecnych release line najbliższe zmiany fazy wypadają w dwóch datach: według grafiku wydań Node.js release line 24 przeszła do fazy Maintenance LTS 20 października, dzień przed wydaniem zabezpieczeń, a release line 26 dostanie wsparcie długoterminowe 28 października. Release line 22 jest w fazie Maintenance LTS od 21 października 2025 roku i straci wsparcie 30 kwietnia 2027 roku, a release line 24 straci je 30 kwietnia 2028 roku.

Praktyczny sens jest prosty: pytanie „czy mamy najnowszą wersję” samo w sobie niewiele mówi, a użyteczne jest pytanie „w której release line jesteśmy i do której daty będzie ona dostawać poprawki”; pierwsze pytanie zmienia odpowiedź co miesiąc, drugie raz na parę lat, i właśnie dlatego to drugie łatwo zapomnieć.

Node.js 18 i 20: poprawki nie będzie

W ogłoszeniu Node.js jest zdanie, które tym razem jest ważniejsze niż wszystkie dziesięć podatności: wersje, dla których wsparcie się skończyło, przy wydaniu zabezpieczeń są zawsze dotknięte, i poprawek się dla nich nie wydaje ani teraz, ani później, bo release line po końcu wsparcia nie dostaje już żadnego wydania.

Według grafiku wydań release line 16 straciła wsparcie 11 września 2023 roku, 19 — 1 czerwca 2023 roku, 18 — 30 kwietnia 2025 roku, a 20 — 30 kwietnia 2026 roku. Ostatnia data jest najważniejsza: release line 20 straciła wsparcie niecałe sześć miesięcy temu, a projekt, który na niej wystartował, nadal może działać bez żadnych widocznych problemów; właśnie to czyni sytuację zdradliwą, bo nic w jej działaniu nie wskazuje, że poprawki zabezpieczeń już jej nie przysługują.

Firmom, które nie mogą szybko przejść na wspieraną release line, ogłoszenie Node.js podaje jedną możliwość: komercyjne wsparcie dla wersji po fazie Maintenance LTS oferują partnerzy OpenJS Ecosystem Sustainability Program, i jest to usługa płatna oraz tymczasowe rozwiązanie, a nie zastępstwo przejścia.

Jak sprawdzić, którego Node.js używa Państwa aplikacja

Sprawdzenie zajmuje minuty, ale jest jedno miejsce, w którym łatwo zrobić to źle: wersja Node.js, którą pokazuje terminal, nie zawsze jest tą, z którą pracuje aplikacja.

Hosting z cPanel: używaną wersję pokazuje sekcja Software → Setup Node.js App, w której każda aplikacja ma własny wiersz, a wersję wybiera się z listy. Na liście są tylko te wersje, które na serwerze zainstalował dostawca hostingu, a polecenie node -v na takim hostingu może pokazać inną wersję niż to ustawienie, więc patrzą Państwo właśnie na właściwy wiersz.

Własny serwer: polecenie node -v pokazuje wersję, która jest pierwsza w ścieżce systemowej, ale menedżer procesów, na przykład PM2 albo usługa systemd, może uruchomić program zupełnie innym plikiem wykonywalnym; najpewniejsza odpowiedź to ta, którą proces sam podaje o sobie, czyli wartość process.version, na przykład zapisana w dzienniku w chwili startu.

Kontenery: w środowisku Docker wersję określa container image, a nie maszyna hosta; container image zbudowany przed 21 października poprawki nie zawiera, niezależnie od tego, ile razy kontener był restartowany, więc trzeba go zbudować od nowa ze zaktualizowanym base image.

Gdy dostępu nie ma: wtedy pytanie jest do dostawcy hostingu albo do dewelopera, który to utrzymuje, a sformułowanie, które skraca korespondencję, brzmi tak: „Której wersji Node.js używa nasza aplikacja? Czy w swojej release line jest co najmniej 22.23.4, 24.22.1 albo 26.12.1? Jeżeli jest na release line bez wsparcia, na przykład 18 albo 20, kiedy planowane jest przejście?”

Co robić dziś

Dzisiejsza kolejność jest taka: najpierw zaktualizować Node.js do poprawionej wersji w swojej release line i zrestartować każdy proces, który go używa, potem przebudować wszystko, w czym runtime jest wbudowany, i dopiero potem planować przejście tym projektom, które działają na release line bez wsparcia.

Po pierwsze, aktualizują Państwo w ramach release line: do 22.23.4, 24.22.1 albo 26.12.1, zależnie od tego, na której release line są Państwo; w przypadku hostingu robi to dostawca, a zadaniem Państwa jest upewnić się, że aplikacja używa zaktualizowanej wersji.

Po drugie, restartują Państwo procesy: proces, który już działa, nadal używa tego Node.js, z którym został uruchomiony, a podmiana pliku na dysku niczego tu nie zmienia, i to samo dotyczyło PHP2Shell, bo pakiet został wymieniony, ale stary proces nadal pracuje na starym kodzie.

Po trzecie, przebudowują Państwo to, w czym runtime jest wbudowany: container image Dockera trzeba zbudować od nowa ze zaktualizowanym base image, a jeżeli program rozprowadzają Państwo jako jeden plik wykonywalny (SEA), jest w nim wbudowany sam Node.js, więc aktualizacja wersji zainstalowanej w systemie go nie rusza i plik trzeba zbudować od nowa z poprawioną wersją, i to samo dotyczy startup snapshot, których dotyczy błąd pamięci podręcznej losowości.

Po czwarte, jeżeli Państwo są na release line 18 albo 20, planują Państwo przejście: to większa praca niż aktualizacja w ramach release line, więc warto zacząć teraz, a kolejność jest opisana w następnej sekcji.

Jak przejść z Node.js 18 albo 20

Przejście między wersjami głównymi nie jest pracą na jedną komendę i warto robić je po kolei, a najpierw trzeba wybrać cel. W większości przypadków jest nim release line 24, która poprawki będzie dostawać do 30 kwietnia 2028 roku, natomiast release line 22 jest rozsądnym wyborem tylko wtedy, gdy jakaś ważna zależność jeszcze nie wspiera release line 24, bo wsparcie release line 22 skończy się rok wcześniej.

Potem trzeba znaleźć wszystkie miejsca, w których projekt ustala swoją wersję Node.js, bo prawie zawsze jest ich kilka: w package.json pole engines, pliki .nvmrc albo .node-version, base image w Dockerfile, konfiguracja środowiska continuous integration (CI) i ustawienie w panelu hostingu. Jeżeli podmieni się tylko część z nich, w różnych środowiskach działają różne wersje, a błędy pojawiają się tam, gdzie najtrudniej je znaleźć.

Potem projekt trzeba w środowisku testowym zainstalować od nowa z nową wersją i uruchomić testy, a szczególna uwaga należy się pakietom, w których jest skompilowany kod: buduje się je pod konkretną wersję Node.js, więc po przejściu może być konieczne ich przebudowanie albo aktualizacja. Node.js dla każdej wersji głównej publikuje listę zmian, w której osobno zaznaczono zmiany mogące zepsuć istniejący kod, i warto ją przeczytać przed przejściem, a nie po nim.

Dopiero wtedy przychodzi środowisko produkcyjne: zmiana wersji, restart procesów i sprawdzenie, że process.version pokazuje nową wersję, a stara wersja może zostać dostępna przez kilka dni na wypadek cofnięcia zmiany, ale nie dłużej, bo właśnie ona jest wersją, dla której poprawki już nie będzie.

Czego nie aktualizuje się razem z Node.js

Aktualizacja Node.js naprawia sam runtime i biblioteki, które są w nim wbudowane, ale nie wszystko, czego używa aplikacja, i właśnie tu najczęściej zostają dziury, których nikt nie widzi.

Osobno instalowane pakiety: jeżeli projekt w swoich zależnościach używa pakietu undici z npm, a nie wbudowanego fetch, ten pakiet leży w katalogu node_modules projektu i wydanie Node.js go nie rusza; to samo dotyczy wszystkich pozostałych zależności, bo aktualizuje je menedżer pakietów, i tylko wtedy, gdy ktoś go uruchomi.

CA store: domyślnie Node.js nie używa listy urzędów certyfikacji systemu operacyjnego, tylko własnej, wbudowanej, a w dokumentacji jest napisane, że to kopia listy Mozilli utrwalona w chwili wydania. Takie zmiany, jak reakcja tego wydania na skompromitowany certyfikat pośredni eMudhra, program dostaje więc tylko razem z aktualizacją runtime, a nie razem z aktualizacjami systemu operacyjnego, chyba że został specjalnie skonfigurowany tak, by używać listy systemowej.

Jak nie przegapić następnego wydania

Wydania zabezpieczeń Node.js są zapowiadane wcześniej na stronie bezpieczeństwa projektu, ze wskazaniem planowanego dnia wydania, i tym razem właśnie tak było, więc ta środa nie była zaskoczeniem dla tych, którzy tę stronę śledzą: aktualizację dało się zaplanować z wyprzedzeniem, a nie szukać na nią czasu tego samego dnia.

W praktyce znaczy to, że ktoś musi odpowiadać za to śledzenie, a w hostingu zarządzanym robi to dostawca, natomiast na własnym serwerze albo w projekcie utrzymywanym przez własny zespół warto wiedzieć, kto dokładnie śledzi ogłoszenia projektu, kto decyduje o aktualizacji i kto sprawdza, że po niej wszystko działa. Jeżeli na te pytania odpowiedź brzmi „nikt konkretny”, właśnie tam jest największe ryzyko, a nie w którejś z dziesięciu podatności.

Jeżeli Node.js służy Państwu tylko jako build tool

Na wielu stronach internetowych na Laravelu i w WordPressie Node.js na serwerze w ogóle nie działa: uruchamia Vite albo podobne narzędzie, które z kodu źródłowego przygotowuje pliki CSS i JavaScript, a potem się zatrzymuje. W takim przypadku sytuacja różni się od aplikacji, która sama obsługuje żądania, bo proces budowania nie odpowiada na żądania z internetu i nie rozpakowuje danych, które przysyłają mu nieznani odwiedzający.

To, że sytuacja jest inna, nie znaczy, że jest to nieważne: te build tool działają na komputerach deweloperów i w środowisku continuous integration (CI), pobierają pakiety i nawiązują połączenia z innymi serwerami, a opisy podatności są zbyt krótkie, żeby powiedzieć z pewnością, które z tych dróg są dotknięte. Zaktualizować Node.js warto także tam, tylko we właściwej kolejności: najpierw serwery, na których Node.js działa online, potem komputery deweloperów i środowisko CI.

Jest też drugi powód, który z tym tygodniem nie ma związku: środowisko budowania często zostaje na tej wersji Node.js, z którą projekt kiedyś wystartował, i wtedy pewnego dnia nowa wersja narzędzia po prostu odmawia z nią pracy. Przejście na wspieraną release line odbywa się wtedy w pośpiechu i w nieodpowiednim momencie, więc łatwiej zrobić je teraz, póki jest zaplanowaną pracą.

Co to znaczyło na naszym hostingu

Nasz hosting zarządzany to miejsce na naszym własnym serwerze, i każdy klient wybiera tam wersję przez panel hostingu. Na naszym serwerze release line Node.js 22 i 24 już działają na poprawionych wersjach 22.23.4 i 24.22.1, a klienci, których projekty używają tych release line, nie muszą nic robić poza upewnieniem się, że proces po aktualizacji został zrestartowany.

Trzeba też uczciwie powiedzieć o drugiej stronie: na serwerze do wyboru nadal są release line, dla których wsparcie się skończyło, i poprawki dla nich nie ma — ani na naszym, ani na jakimkolwiek innym serwerze, bo Node.js takiej poprawki nie wydaje. Jeżeli dla Państwa projektu wybrano którąś z nich, aktualizacja oznacza przejście na release line 22 albo 24, i najlepiej zrobić je razem ze sprawdzeniem, że po przejściu wszystko działa tak samo jak wcześniej.

Nasz Managed Hosting zaczyna się od 45 € miesięcznie, a utrzymywane runtime są częścią tego, za co w tej kwocie się płaci, i jeżeli Państwa aplikacja nadal działa na Node.js 18 albo 20, porozmawiamy o przejściu, zanim wymusi je dzień któregoś następnego wydania zabezpieczeń.

FAQ

Często zadawane pytania.

Która wersja Node.js jest bezpieczna?

Bezpieczne są 22.23.4, 24.22.1 i 26.12.1 albo nowsze wersje w tej samej release line. Wszystko, co w swojej release line jest starsze, jest dotknięte. Dla wersji, których release line ma już za sobą koniec wsparcia, na przykład Node.js 18 i 20, bezpiecznej wersji nie ma wcale, bo poprawek się dla nich nie wydaje.

Mam Node.js 18 albo 20. Co robić?

Dla obu release line wsparcie się skończyło: dla release line 18 30 kwietnia 2025 roku, dla release line 20 30 kwietnia 2026 roku. Poprawki dla nich nie ma i nie będzie, więc jedynym rozwiązaniem jest przejście na wspieraną release line. Release line 24 będzie dostawać poprawki do 30 kwietnia 2028 roku, release line 22 do 30 kwietnia 2027 roku. Przejście warto najpierw wypróbować w środowisku testowym.

Czy ta aktualizacja zabezpieczeń Node.js to to samo co PHP2Shell?

Nie. Oba zdarzenia były 21 października, ale są osobne. PHP2Shell to dwie krytyczne podatności w samym PHP. Wydanie zabezpieczeń Node.js było zapowiedziane wcześniej, jego najpoważniejsze podatności oceniono jako wysokie, a w ogłoszeniu nie ma mowy o atakach w rzeczywistym środowisku.

Czy wystarczy zaktualizować Node.js, jeżeli aplikacja już działa?

Nie, proces trzeba zrestartować. Proces, który już działa, nadal używa tego Node.js, z którym został uruchomiony. Jeżeli aplikacja jest w container image Dockera albo jest rozprowadzana jako jeden plik wykonywalny (SEA), jest w niej wbudowany sam Node.js i trzeba ją przebudować z poprawioną wersją.

Na naszej witrynie na Laravelu albo w WordPressie Node.js służy tylko do budowania w Vite. Czy to nas dotyczy?

Mniej niż aplikację, która sama obsługuje żądania, bo proces budowania nie odpowiada na żądania z internetu. Te build tool pobierają jednak pakiety i nawiązują połączenia z innymi serwerami, więc Node.js warto zaktualizować także na komputerach deweloperów i w środowisku CI, tylko po serwerach.

Czy są tymczasowe rozwiązania, jeżeli nie da się zaktualizować od razu?

W ogłoszeniu Node.js tymczasowych rozwiązań nie wymieniono. Jedynym zaleceniem jest używanie wspieranej wersji według grafiku wydań. Dla wersji, dla których wsparcie się skończyło, Node.js wspomina wsparcie komercyjne, które oferują partnerzy OpenJS Ecosystem Sustainability Program.

Jak ustalić, której wersji Node.js używa aplikacja na hostingu z cPanel?

W sekcji Software → Setup Node.js App, w której każda aplikacja ma własny wiersz z wybraną wersją. Polecenie node -v w terminalu może pokazać inną wersję niż ustawienie aplikacji, więc patrzą Państwo właśnie na wiersz aplikacji.

POWIĄZANA USŁUGA
Zarządzany hosting i infrastruktura serwerowa

Serwery, hosting i opieka w UE — we Frankfurcie, Helsinkach lub Rydze, z monitoringiem 24/7. Pełne zarządzanie: aktualizacje, bezpieczeństwo i wydajność, automatyczne kopie zapasowe z testowanym odtwarzaniem, certyfikaty SSL, CDN i ochrona przed DDoS. Przenosimy z obecnego hostingu bez przestoju i skalujemy według obciążenia.

Więcej informacji