Ostatnio sporo słychać o kolejnych CVE dla Next.js, o lukach w popularnych frameworkach, o tym że "React jest dziurawy" i że trzeba "uciekać na inne technologie". Panika wśród developerów jest spora. Pytanie — czy uzasadniona?
Zanim zaczniemy — jedna teza, którą chcę Wam wbić do głowy na wstępie:
Nie ma bezpiecznego frameworka. Jest bezpieczny lub niebezpieczny developer.
Okej, rozłóżmy to na czynniki pierwsze.
AI obniżyło poprzeczkę. I to mocno.
Jeszcze pięć lat temu znalezienie poważnej luki w aplikacji webowej wymagało naprawdę dobrego warsztatu. Reverse engineering, znajomość wzorców podatności, ręczne fuzzowanie, lata doświadczenia. To był klub dla wybranych.
Dziś mamy modele językowe, które potrafią przeanalizować kod, znaleźć wzorce podatności i zasugerować exploita w kilka minut. Mamy narzędzia, które skanują repozytoria, korelują zależności z bazami CVE i flagują problemy automatycznie. Bariera wejścia dla atakującego dramatycznie spadła.
W wielkim skrócie — kiedyś żeby skrzywdzić aplikację, trzeba było wiedzieć co robisz. Teraz wystarczy wiedzieć, czego chcesz.
To nie znaczy, że każdy script kiddie nagle stał się pentesterem. Ale to znaczy, że powierzchnia ataku jest teraz wystawiana na znacznie więcej oczu. I te oczy szukają aktywnie.
Nowoczesne frameworki są złożone. Złożoność to wróg bezpieczeństwa.
Pamiętacie czasy prostego HTML z PHP? Jeden plik, jedna odpowiedzialność, zero magii. Dzisiaj mamy SSR, RSC (React Server Components), Server Actions, Edge Runtime, middleware, cache layers, hydration, ISR, PPR... i to tylko w Next.js.
Każda warstwa abstrakcji to potencjalne miejsce, gdzie coś może pójść nie tak. Nie dlatego, że twórcy frameworka są niedbali — ale dlatego, że złożoność systemu rośnie szybciej niż zdolność do przewidzenia wszystkich edge case'ów.
Dodajmy do tego ekosystem npm z setkami tysięcy paczek, z których każda ma własne zależności, własnych maintainerów i własną historię podatności. Jeden zainfekowany pakiet kilka poziomów głębiej w drzewie zależności — i masz supply chain attack na produkcji.
Haczyk polega na tym, że to nie jest problem frameworka samego w sobie. To jest problem skali i złożoności ekosystemu, który wokół niego wyrósł.
Dlaczego Next.js dostaje najbardziej po głowie?
Często słyszę: "Next.js jest dziurawy, bo Vercel nie dba o bezpieczeństwo". To nieprawda i trzeba to powiedzieć wprost.
Next.js dostaje dużo CVE z jednego głównego powodu — jest najpopularniejszym frameworkiem SSR na świecie. A popularność przyciąga uwagę. Zarówno badaczy bezpieczeństwa, którzy aktywnie szukają luk (to dobrze), jak i atakujących (to mniej dobrze).
Ale jest też druga strona medalu — Next.js jest wyjątkowo rozbudowany. Server Actions, middleware na Edge, własny cache, integracja z React Server Components. Im więcej ruchomych części, tym więcej potencjalnych styków między nimi, gdzie może pojawić się błąd.
Jeden z ostatnich głośnych przypadków — podatność w middleware Next.js, która przy odpowiednio spreparowanym nagłówku pozwalała ominąć autoryzację. To nie był błąd Reacta. To był błąd w bardzo specyficznym miejscu, w bardzo specyficznej konfiguracji. Ale wystarczył, żeby rozruszać pół Twittera.
Tak czy inaczej — Next.js jest popularny, złożony i aktywnie analizowany. Taka jest cena bycia liderem.
Top 20 zagrożeń podczas developmentu.
Dobra, czas na konkrety. Poniżej moja lista top 20 największych zagrożeń i najgorszych praktyk (z mojej listy zawierającej równe 100 takich rzeczy, na które trzeba uważać), które sprawiają, że jest się łatwym kąskiem dla takich ataków i wykorzystywania luk.
1. Brak lockfile albo ignorowanie go w CI npm install zamiast npm ci w pipeline'ie to klasyk. Build nie jest powtarzalny, dzisiaj instalujesz jedną wersję zależności, jutro inną. Przy odrobinie pecha — złośliwą.
2. Automatyczny merge aktualizacji dependencies bez review Dependabot i Renovate są świetne. Automerge bez testów i code review — już mniej. Wadliwa albo złośliwa wersja paczki może wejść na produkcję szybciej niż ktokolwiek to zauważy.
3. Zbyt luźne wersje paczek "package": "*" albo "package": "latest" w package.json to proszenie się o kłopoty. Sam ^ nie jest złem absolutnym, ale bez lockfile i kontroli aktualizacji robi się niebezpieczny.
4. Instalowanie małych, losowych paczek z npm Paczka do jednej funkcji, mały helper, randomowy komponent UI znaleziony na GitHubie. Każda paczka to dodatkowy maintainer, dodatkowe zależności i dodatkowa powierzchnia ataku. To jest szczególnie ciekawy kąt — ataki nie idą przez npm, tylko przez środowisko developera. Wykrycia złośliwych rozszerzeń VS Code prawie się poczwórzyły w 2025 roku — z 27 przypadków w 2024 do 105 w pierwszych dziesięciu miesiącach 2025.

W listopadzie 2025 pojawiło się rozszerzenie prettier-vscode-plus — podszywające się pod popularny Prettier. Po instalacji uruchamiało wieloetapowy łańcuch ataków kończący się pełnym zdalnym dostępem do maszyny dewelopera, kradzieżą tokenów, kluczy SSH i secrets z CI/CD.5. Brak review zmian w lockfile W PR-ze wszyscy patrzą na komponenty, a package-lock.json ma 4000 linii i nikt go nie czyta. A to właśnie tam widać nowe transitive dependencies albo podejrzane podmiany wersji.Niedawny przykład - wrzesień 2025: atak na konta maintainerów chalk, debug, ansi-styles i 15 innych paczek z łącznym zasięgiem ponad 2.6 miliarda pobrań tygodniowo. Wektor ataku był banalny — phishing podszywający się pod support npm, który skłonił maintainera do zresetowania credentials i 2FA. Każdy projekt z automerge wciągnął złośliwą wersję automatycznie w ciągu tych 2 godzin.
6. Sekrety w frontendzie NEXT_PUBLIC_* i VITE_* są publiczne. Wiem, że to brzmi oczywisto. A jednak podobno co tydzień ktoś wypycha na produkcję token API w envie, który ląduje w bundlu klienta.
7. Tokeny w localStorage Wygodne, popularne i ryzykowne. Przy XSS atakujący może ukraść token i przejąć sesję. Znacznie bezpieczniejszy model to cookies HttpOnly, Secure, SameSite.
8. Renderowanie HTML/Markdown bez sanitizacji dangerouslySetInnerHTML, opisy produktów z CMS-a, komentarze użytkowników, WYSIWYG. Bez sanitizacji to klasyczna brama dla XSS. Znany przykład British Airways, 2018. Atakujący dostali się przez skompromitowane dane logowania dostawcy zewnętrznego, zmodyfikowali plik JavaScript na stronie i przez 16 dni skrymer zbierał dane kart płatniczych klientów. ICO nałożyło karę 20 milionów funtów.
9. Brak Content Security Policy CSP nie naprawia XSS, ale dramatycznie ogranicza jego skutki. Bez CSP jeden błąd w renderowaniu treści może pozwolić na wykonanie złośliwego JavaScriptu na stronie.
10. Poleganie wyłącznie na walidacji po stronie frontendu Frontendowa walidacja jest dla UX, nie dla bezpieczeństwa. Użytkownik może ominąć UI i wysłać request ręcznie. Backend musi walidować dane, role, limity i ownership. Zawsze.
11. Brak backendowej autoryzacji dla zasobów Ukrycie przycisku "Usuń" w React nie zabezpiecza endpointu. IDOR, privilege escalation, dostęp do cudzych danych po zmianie userId w parametrze — klasyki, które wciąż żyją.
12. Błędny caching w SSR/Next.js Next.js agresywnie cachuje odpowiedzi Server Components i Route Handlerów. Problem pojawia się, gdy zapomnisz oznaczyć jako dynamiczne dane, które są per-user.
Przykład: masz Server Component który fetchuje dane zalogowanego użytkownika. Bez jawnego cache: 'no-store' albo cookies()/headers() w środku, Next.js może zcachować tę odpowiedź. Kolejny użytkownik wchodzi na tę samą stronę i dostaje... dane poprzednika. Imię, zamówienia, adres.
To nie jest teoretyczny scenariusz — to realny błąd, który pojawia się gdy piszesz Server Components jak zwykłe komponenty, nie myśląc o tym że cache w Next.js działa inaczej niż cache przeglądarki. Sam padłem jego ofiarą na początku przygody z Next.js
13. Publiczne source mapy na produkcji Source mapy pomagają debugować lokalnie. Publicznie — ujawniają strukturę aplikacji, nazwy funkcji, logikę biznesową i endpointy. Ułatwiają reverse engineering. A daleko nie trzeba szukać jeśli chodzi o przykłady. W marcu 2026 roku Anthropic przypadkowo wypuściło na npm pełne źródła Claude Code — 512 000 linii TypeScript — przez plik source mapy (.map) dołączony do paczki wersji 2.1.88. Jednocześnie wyszło na jaw jakim chaotycznym software jest Claude Code.

14. Logowanie danych wrażliwych console.log(user), console.log(token), pełne odpowiedzi API w Sentry. Tokeny, dane osobowe i klucze API lądują w logach, które często mają szeroki dostęp w zespole.
15. Niebezpieczne przekierowania Redirect po loginie na URL wzięty bez walidacji z parametru next. Open redirect, phishing, przechwytywanie flow logowania — i nagle prosta funkcja "wróć po zalogowaniu" staje się problemem.
16. Brak testów krytycznych ścieżek Większość projektów ma testy — problem w tym, co jest testowane. Sprawdzamy czy komponent się renderuje, czy przycisk ma właściwy kolor, czy modal otwiera się po kliknięciu. To łatwe do napisania i daje zielone checkmarki w CI.
Tymczasem nikt nie napisał testu który sprawdza, czy użytkownik bez roli admina naprawdę nie może usunąć cudzego zamówienia. Albo czy po nieudanej płatności stan koszyka wraca do normy. Albo czy reset hasła faktycznie unieważnia stary token po użyciu.
Efekt: regresje w tych miejscach wychodzą na produkcji, nie w pipeline'ie. A to są akurat miejsca, gdzie błąd kosztuje — finansowo, wizerunkowo albo prawnie.
17. Uruchamianie kodu z PR-ów z dostępem do sekretów CI/CD Szczególnie w open source albo przy forkach. Złośliwy PR może wyciągnąć sekrety z pipeline'u zanim ktokolwiek zrobi review.
18. Zbyt szerokie uprawnienia tokenów CI/CD Token deployowy z dostępem do wszystkiego. Po wycieku jednego sekretu — dostęp do publikacji paczek, deployów albo całej infrastruktury.
19. Brak kontroli nad third-party scripts Każdy zewnętrzny skrypt — analytics, chat widget, piksel reklamowy — wczytany przez <script> ma pełny dostęp do DOM-u, formularzy i storage Twojej aplikacji, dokładnie tak samo jak Twój własny kod. Problem nie leży nawet w samych dostawcach, bo renomowane firmy zazwyczaj są w porządku — leży w ich zależnościach i w tym, że domeny i pakiety zmieniają właścicieli. Klasyczny przykład: Polyfill.io, skrypt wczytywany przez setki tysięcy stron, po sprzedaży domeny zaczął serwować złośliwy kod użytkownikom — bez żadnej zmiany w kodzie tych stron.
20. Odkładanie aktualizacji frameworka i paczek latami Całkowity brak aktualizacji to proszenie się o kłopoty — stare CVE, martwe biblioteki, migracja przez trzy major wersje naraz gdy wreszcie coś zmusi do działania. Ale automatyczne aktualizacje bez review to drugi kraniec tej samej głupoty. Najlepsze podejście: Dependabot otwiera PR-y, CI je waliduje, człowiek podejmuje decyzję.

A może po prostu zmienić framework?
Jasne, rozumiem ten odruch. Skoro Next.js ma CVE, to może Astro albo SvelteKit będą spokojniejsze? Przyjrzyjmy się realnym alternatywom.
Astro
Astro gra w zupełnie inną grę. Domyślnie zero JavaScript po stronie klienta — tzw. islands architecture. Wysyłasz statyczny HTML, a interaktywność dodajesz tylko tam, gdzie naprawdę jej potrzebujesz. Możesz mieszać Reacta, Vue, Svelte w jednym projekcie.
Za: bardzo mały bundle, doskonały Lighthouse od razu, świetna integracja z headless CMS-ami (Sanity, Contentful), mniejsza powierzchnia ataku przez sam brak JSu po stronie klienta.
Przeciw: przy złożonych, mocno interaktywnych aplikacjach zaczynasz walczyć z narzędziem, nie pracować z nim. Brak pełnego SSR na poziomie Next.js. Mniejszy ekosystem integracji z e-commerce.
Dla kogo: strony marketingowe, blogi, landing page'e, portale z dużą ilością statycznego contentu. Jeśli Astro to takie zastosowanie jest najlepszym wyborem.
TanStack Start
Budowany przez Tannera Linsleya — twórcę react-query i react-table, które są w połowie ekosystemu Reacta. TanStack Start to pełny framework SSR/SSG, ale zbudowany z obsesją na typesafety i bez ukrytej logiki. Zero "magii", wszystko jasne i przewidywalne.
Za: wyjątkowa typesafety od samego dołu, routing który faktycznie rozumiesz, silna społeczność wokół ekosystemu TanStack.
Przeciw: wciąż młody — nie bierz go na duży projekt produkcyjny bez świadomości ryzyka. Ekosystem integracji (CMS, e-commerce, auth) jeszcze nie dorósł do poziomu Next.js.
Dla kogo: nowe projekty, gdzie możesz przyjąć ryzyko early adoptera. Aplikacje webowe i dashboardy gdzie typesafety jest kluczowe.
SvelteKit
Svelte kompiluje się do czystego vanilla JS — brak virtual DOM, mniejszy bundle, szybsze działanie. SvelteKit to pełny framework SSR na bazie Svelte z własnym routingiem i konwencjami.
Za: inny poziom wydajności runtime'owej, bardzo czytelny kod, mała ilość boilerplate'u, aktywnie rozwijany, rosnąca popularność.
Przeciw: musisz nauczyć się Svelte — to inny świat niż React. Ekosystem mniejszy, mniej gotowych integracji, mniej specjalistów na rynku jeśli budujesz zespół.
Dla kogo: projekty gdzie masz czas wejść w nową technologię i zależy Ci na wydajności oraz czystości kodu. Świetny do nowych projektów greenfield.
Remix / React Router v7
Po fuzji Remix i React Router to teraz jedna rzecz. Model danych oparty na loaders i actions, bardzo bliski webowym standardom (Web Fetch API), świetny do aplikacji z dużą ilością formularzy i mutacji danych.
Za: bardzo dobry UX model, progresywne ulepszanie działa out of the box, React pod spodem więc ten sam skill.
Przeciw: mniejszy ekosystem niż Next.js, mniej hostingów obsługuje go natywnie, niektóre konwencje wymagają przestawienia głowy.
Dla kogo: aplikacje z heavy data fetching i mutacjami, e-commerce gdzie zależy na progresywnym ulepszaniu.
Więc czy powinniśmy się bać?
Nie. Ale powinniśmy być poważni.
Pomimo ostatnich CVE, pomimo rosnącej liczby ataków, pomimo AI, który obniżył poprzeczkę dla atakujących — ryzyko jest do opanowania. Pod warunkiem, że traktujemy bezpieczeństwo jako element procesu, a nie afterthought.
Kilka konkretnych rzeczy, które możesz wdrożyć jutro:
- Skonfiguruj Dependabot albo Renovate z wymogiem code review zamiast automerge
- Włącz GitHub Security Alerts albo odpowiednik w swoim repo
- Subskrybuj security advisories dla frameworków, których używasz — Next.js ma oficjalny kanał, większość dużych projektów też
- Ustaw npm audit albo odpowiednik jako krok w CI/CD — jeśli audit failuje, build failuje. Choć to dość drastyczny krok.
- Ogarnij CSP choćby w trybie report-only na początku

Zmiana frameworka nie rozwiąże problemów bezpieczeństwa, jeśli procesy w zespole są dziurawe. Astro z fatalnym CI/CD będzie mniej bezpieczne niż Next.js z porządnymi alertami i kulturą review.
Ryzyko zawsze istnieje — niezależnie od frameworka, języka i chmury. Różnica jest w tym, jak aktywnie tym ryzykiem zarządzasz.
A Next.js, mimo wszystkich CVE z ostatnich miesięcy, ma jeden argument za sobą — jest aktywnie utrzymywany, podatności są szybko łatane, a team Vercela podchodzi do security advisories profesjonalnie. To nie jest framework porzucony. To jest framework, który dostaje po głowie dokładnie dlatego, że jest wszędzie.
Pilnuj paczek. Aktualizuj regularnie. Monitoruj alerty. I może nie odkładaj tej aktualizacji Next.js "na przyszły tydzień".


