Nanoid CVE-2026-73086: jeden rozmiar może uczynić tokeny przewidywalnymi
Przepełnienie 32-bitowej liczby psuje globalny offset puli CSPRNG. Kolejne identyfikatory mogą stać się stałym ciągiem, więc sama aktualizacja nie zawsze wystarczy.
- AUTOR
- Karol Rapacz / CEO Breachroad · OSCP · PNPT
- PUBLIKACJA
- 11 sierpnia 2026
- CZAS CZYTANIA
- 12 min czytania
- TEMAT
- Łańcuch dostaw
CVE-2026-73086, opublikowane 11 sierpnia 2026 roku, dotyczy Nanoid — popularnego generatora krótkich identyfikatorów dla JavaScriptu. Podanie do nanoid(size) odpowiednio dużego rozmiaru może doprowadzić do konwersji na ujemną 32-bitową liczbę i uszkodzić globalny offset puli losowej. Skutek jest poważniejszy niż nieudane pojedyncze wywołanie: kolejne identyfikatory generowane w tym samym procesie mogą stać się deterministycznym ciągiem uuuuuuuuuuuuuuuuuuuuu aż do restartu.
Podatne są wersje wcześniejsze niż 3.3.12 oraz wersje od 4.0.0 do wcześniejszych niż 5.1.11. Advisory projektu ocenia problem na 7,4 w CVSS 3.1. Warunkiem jest możliwość wpłynięcia na parametr size; domyślne wywołanie bez danych użytkownika nie wyzwala błędu. Jednak po wyzwoleniu wpływ może objąć inne, pozornie niezwiązane żądania obsługiwane przez ten sam proces.
Co dzieje się z pulą losowości
Nanoid dla wydajności utrzymuje procesową pulę bajtów pobranych z kryptograficznego generatora systemowego. Kolejne identyfikatory korzystają z fragmentów puli, a poolOffset wskazuje bieżącą pozycję. To normalna optymalizacja, o ile arytmetyka offsetu zachowuje poprawny zakres.
W podatnej wersji rozmiar mógł zostać sprowadzony do podpisanej 32-bitowej liczby. Wartość 2147483648, czyli 2^31, po takiej konwersji staje się -2147483648. Wywołanie wchodzi do fillPool() z parametrem, którego autorzy nie zakładali. Globalny offset zostaje przesunięty poza prawidłowy stan, a następne odczyty nie czerpią oczekiwanych, świeżych bajtów losowych.
W efekcie generator zwraca powtarzalną wartość złożoną z liter u. Nie oznacza to matematycznego złamania systemowego CSPRNG. Źródło losowości nadal jest mocne; błąd leży w zarządzaniu buforem i arytmetyce indeksów. Ta różnica ma znaczenie dla naprawy: potrzebna jest walidacja rozmiaru i poprawa logiki, a nie wymiana systemowego generatora.
Kiedy identyfikator jest sekretem
Nanoid bywa używany do nazw rekordów, publicznych slugów, identyfikatorów korelacyjnych i kluczy cache. Przewidywalność takich wartości może powodować kolizje lub błędy dostępności, ale nie zawsze daje przejęcie konta. W innych systemach ten sam generator tworzy token resetu hasła, sekret zaproszenia, identyfikator sesji, klucz API albo token CSRF. Wtedy utrata nieprzewidywalności staje się problemem bezpieczeństwa.
Inwentaryzacja musi więc odpowiedzieć na dwa pytania: czy atakujący kontroluje size, oraz czy późniejsze wyniki Nanoid pełnią funkcję uwierzytelniającą lub autoryzacyjną. Sama obecność pakietu w package-lock.json nie rozstrzyga osiągalności. Z kolei wysoka zagnieżdżona pozycja zależności nie oznacza niskiego ryzyka, jeżeli framework używa jej do sesji.
Najgroźniejszy scenariusz jest międzyżądaniowy. Jedno żądanie z ogromnym rozmiarem zatruwa proces, a token wydany później innej osobie jest stały. Monitoring oparty tylko na odpowiedzi dla żądania atakującego może tego nie zauważyć. Trzeba skorelować zdarzenie z identyfikatorami generowanymi do restartu lub naprawy procesu.
Co dokładnie naprawiono
Projekt opublikował poprawki w Nanoid 3.3.12 oraz Nanoid 5.1.11. Organizacje pozostające na wspieranej gałęzi 3.x mogą przejść na 3.3.12, a użytkownicy 5.x na co najmniej 5.1.11. Zakres 4.x również jest podatny; migracja powinna prowadzić do aktualnej poprawionej gałęzi z uwzględnieniem zmian kompatybilności.
Wydania z poprawką pojawiły się wcześniej niż publiczny rekord CVE. To częsty wzorzec skoordynowanego ujawnienia. Skaner może zacząć alarmować 11 sierpnia, choć pakiet można było zaktualizować już wcześniej. Dlatego SBOM powinien być stale porównywany z advisory projektu, a nie wyłącznie miesięcznym eksportem NVD.
Nie należy kopiować walidacji jako przypadkowego limitu w jednym kontrolerze. Wartość musi być sprawdzana przy publicznej granicy API biblioteki, zanim jakakolwiek arytmetyka zmieni stan globalny. Parametr powinien być skończoną, dodatnią liczbą całkowitą w udokumentowanym zakresie. Każdy wrapper aplikacyjny może dodatkowo narzucić mniejszy limit wynikający z biznesu.
Plan reakcji i ograniczenia wpływu
- Wyszukaj Nanoid w produkcyjnych obrazach i bundle’ach oraz ustal efektywną wersję po rozwiązaniu zależności.
- Zidentyfikuj wszystkie wywołania z argumentem
size, szczególnie pochodzącym z parametrów HTTP, konfiguracji tenantów, plików lub komunikatów kolejki. - Ustal, gdzie wyniki służą jako sekrety, tokeny jednorazowe, identyfikatory sesji albo wartości chroniące przed CSRF.
- Zaktualizuj do
3.3.12,5.1.11lub nowszego bezpiecznego wydania właściwej gałęzi i przebuduj wszystkie obrazy. - Zatrzymaj stare procesy po wdrożeniu. Hot reload kodu nie musi wyzerować uszkodzonego stanu modułu w każdym runtime.
- Przeszukaj logi i bazę pod kątem deterministycznego ciągu oraz nietypowych duplikatów. Uważaj, aby nie zapisywać pełnych prawidłowych tokenów w logach.
- Unieważnij lub rotuj wartości bezpieczeństwa wydane po możliwym wyzwoleniu, jeżeli nie można wiarygodnie dowieść ich losowości.
Kolejność aktualizacja–restart–rotacja ma znaczenie. Restart przed zmianą biblioteki chwilowo naprawi stan, ale następne złośliwe wywołanie ponownie go zatruje. Wydanie nowych tokenów przed usunięciem podatności może stworzyć kolejną przewidywalną serię. Najpierw zamknij wejście, potem uruchom czysty proces i dopiero wtedy wydawaj nowe sekrety.
W systemie wieloinstancyjnym trzeba ustalić, który pod lub worker obsługiwał żądanie. Stan puli jest procesowy, więc jedna replika może generować wadliwe wartości, a pozostałe działać poprawnie. Identyfikator instancji w telemetrii pomaga zawęzić okno, ale load balancer i automatyczne restarty mogą utrudnić rekonstrukcję.
Detekcja i testy regresji
Najbardziej precyzyjnym sygnałem jest pojawienie się znanego stałego identyfikatora lub nagły wzrost kolizji. Można alarmować na powtarzalność wygenerowanych wartości w krótkim oknie, nie zapisując ich pełnej treści: wystarczy bezpieczny HMAC lub metryka unikalności. Dla tokenów resetu alarm powinien prowadzić do blokady wydawania, a nie tylko biletu na później.
Test regresji powinien uruchomić nieprawidłowy rozmiar, oczekiwać kontrolowanego wyjątku, a następnie sprawdzić, że seria zwykłych identyfikatorów nadal ma unikalność i prawidłowy format. Warto testować granice wokół 2^31, wartości ujemne, NaN, nieskończoność, stringi i bardzo duże liczby. Test ma potwierdzić, że błędne wejście nie zmienia globalnego stanu.
Na poziomie projektu istotna jest zasada: funkcja kryptograficzna musi odrzucić błędny parametr atomowo. Nie może najpierw zmodyfikować współdzielonego bufora, a dopiero potem zwrócić błąd. Stan bezpieczeństwa nie powinien zależeć od historii nieudanych wywołań pochodzących od różnych użytkowników.
Fakty i wnioski
Zakres wersji, stały wynik, wartość wyzwalająca i poprawione wydania pochodzą z advisory projektu oraz rekordu CVE. Źródła nie wskazują masowej kampanii. Breachroad wnioskuje, że incydent wymaga analizy tokenów wydanych po wyzwoleniu, ponieważ aktualizacja nie unieważnia wartości już zapisanych w bazie lub wysłanych użytkownikom.
Warto udokumentować również kryterium zamknięcia: wszystkie produkcyjne obrazy mają bezpieczną wersję, stare procesy zostały zakończone, test graniczny nie zmienia stanu, a tokeny z podejrzanego okna wygasły lub zostały unieważnione. Dopiero taki zestaw dowodów zamyka zarówno podatność, jak i jej potencjalny skutek biznesowy.
Przypadek Nanoid świetnie pokazuje deweloperom, dlaczego bezpieczeństwo zależności to nie tylko aktualizowanie numerów. Trzeba rozumieć osiągalność, rolę wyniku i stan procesu. Te umiejętności rozwijamy podczas szkoleń cyberbezpieczeństwa dla organizacji. Weryfikację zarządzania zależnościami, tokenami i sesją można pogłębić przez testy penetracyjne web i API.


