< Powrót do Artykułów
◆ Case study · Next.js / SSR

Frontend zdradził
mapę backendu.

Techniczne case study z pentestu Next.js: __NEXT_DATA__, bundle JavaScript, ukryte endpointy, nadmiarowe propsy, request/response i rekomendacje dla zespołów dev.

TRYB black-boxOBSZAR Next.js / SSRRYZYKO data exposureEFEKT mapa API i role
HTML
stan aplikacji
w źródle
JS
endpointy
w bundle
API
ukryta trasa
odpowiadała
FIX
minimalizacja
propsów

Frontend potrafi zdradzić architekturę backendu

Next.js, SSR i hydratacja są świetne dla UX, ale z perspektywy pentestera mają jedną cechę: dużo stanu aplikacji trafia do HTML albo bundle JavaScript. Nie wszystko jest podatnością. Ale nadmiarowe dane w __NEXT_DATA__, feature flagach i propsach potrafią odsłonić endpointy, role, identyfikatory, logikę biznesową i czasem tokenopodobne wartości.

Ten case jest zanonimizowany i syntetyczny. Nie pokazujemy prawdziwego HTML klienta, nazw API, hostów ani wartości. Pokazujemy metodykę, przykładowe wzorce i to, jak z pozornie nudnego HTML-a zrobić sensowny rekonesans techniczny.

Pierwszy request bez logowania

sanitized HTML snippet
GET /offers/search HTTP/2 Host: app.example.invalid HTTP/2 200 OK <script id="__NEXT_DATA__" type="application/json"> { "props": { "pageProps": { "initialState": { "apiBase":"https://api.example.invalid/v2", "featureFlags":{"newBilling":true,"internalExport":false}, "viewer":{"role":"anonymous"}, "filters":["city","rate","availability"] } } }, "buildId":"build_REDACTED" } </script>

Na tym etapie nie ma jeszcze „luki”. Jest mapa. Wiemy, gdzie jest API, jakie flagi istnieją, jakie filtry obsługuje backend i jak aplikacja nazywa role. To skraca rekonesans i pozwala pisać celowane testy zamiast ślepego fuzzingu.

Szukamy danych, których nie powinno być w HTML

local analysis commands
# Komendy są ogólne i bezpieczne; nie zawierają hostów klienta. grep -Rho "__NEXT_DATA__" saved-pages/ | wc -l grep -RhoE "(apiBase|featureFlags|internal|admin|token|secret|email|phone)" saved-pages/ | sort -u grep -RhoE "https?://[^"' ]+" saved-pages/ bundles/ | sort -u # Cel: odróżnić dane potrzebne do renderowania od danych nadmiarowych.

Dobre znaleziska to nie „znaleźliśmy słowo admin”. Dobre znalezisko to np. endpoint eksportu, który nie jest widoczny w UI, ale istnieje w propsach; flaga funkcji wewnętrznej; ID tenantów; nazwy ról; ukryte statusy workflow; albo dane użytkowników, które nie są renderowane, ale są w stanie strony.

Routing i endpointy z klienta

sanitized bundle discoveries
/static/chunks/app-build_REDACTED.js route: /api/v2/offers/{offerId}/attachments route: /api/v2/users/{userId}/public-profile route: /api/v2/internal/export-preview enum Role = ["anonymous","user","moderator","admin"] enum OfferStatus = ["draft","published","archived","blocked","review_required"] [!] internal/export-preview nie był linkowany w UI, ale backend odpowiadał.
Wniosek

Bundle JavaScript to dokumentacja API napisana przez frontend. Nie jest problemem sam fakt istnienia tras. Problem zaczyna się wtedy, gdy backend ufa temu, że trasy „nie są w UI”.

Endpoint ukryty w bundle nie oznacza podatności. Trzeba go sprawdzić.

request/response — hidden route validation
GET /api/v2/internal/export-preview?offerId=off_REDACTED HTTP/2 Authorization: Bearer user_token_REDACTED HTTP/2 200 OK { "offerId":"off_REDACTED", "status":"draft", "ownerId":"user_REDACTED", "internalScore":72, "reviewNotes":"redacted synthetic note" } Expected: 403 for non-internal role albo brak danych wewnętrznych Actual: 200 OK z polami spoza UI

Tu pojawił się realny problem: endpoint zwracał dane procesu wewnętrznego zwykłemu użytkownikowi. Źródłem rekonesansu był Next.js, ale podatność była klasyczna: brak autoryzacji i nadmiarowe dane w API.

Severity: Medium / High

Waga zależy od pól. Same feature flagi to zwykle informacja pomocnicza. Dane wewnętrzne, statusy moderacji, notatki, e-maile, telefony, identyfikatory tenantów lub endpointy eksportu mogą podnieść ryzyko do High.

__NEXT_DATA__ to nie vulnerability scanner.
To mapa, która pokazuje, gdzie scanner ma patrzeć.

Checklist pentestera dla Next.js/SSR

DATA

Nadmierne propsy

Czy HTML zawiera pola niewidoczne w UI: e-maile, telefony, role, statusy, notatki, ceny, ID tenantów.

MAPOWANIE → Excessive Data Exposure
API

Endpointy z bundle

Czy trasy znalezione w JS mają poprawną autoryzację i walidację po stronie serwera.

MAPOWANIE → Broken Access Control
FLAGS

Feature flags

Czy flagi ujawniają funkcje wewnętrzne, nazwy eksperymentów, segmenty klientów albo logikę płatności.

MAPOWANIE → Information Disclosure
BUILD

Build artifacts

Czy sourcemapy, stare buildy, preview deployments albo publiczne assety nie odsłaniają kodu i komentarzy.

MAPOWANIE → Security Misconfiguration

Nie walcz z Next.js. Walcz z nadmiarem danych.

1. Minimalizuj pageProps. Do HTML wysyłaj tylko to, co konieczne do renderowania bieżącego widoku.

2. Autoryzuj każdy endpoint. Ukrycie endpointu w UI nie jest zabezpieczeniem. Backend musi sprawdzać rolę, właściciela i tenant.

3. Wyłącz sourcemapy publicznie. Jeżeli sourcemapy są potrzebne, ogranicz dostęp i kontroluj proces publikacji.

4. Przeglądaj bundle przed release. Automatyczny grep po słowach typu internal, admin, token, secret, apiBase, featureFlags potrafi wyłapać regresje.

CI check example
fail build when public assets contain: - sourceMappingURL pointing to public .map - private API hostnames - token-like literals - internal admin routes in client bundle without explicit approval - pageProps fields marked sensitive in schema
◆ BSTA · testpenetracyjny.pl

Masz Next.js, React SSR albo frontend z dużym bundle?

Sprawdzimy, czy frontend nie publikuje mapy backendu, danych wewnętrznych, endpointów i funkcji, których nie powinien widzieć zwykły użytkownik.

Zamów test bezpieczeństwa →
Zakres + wycena w 2 dni robocze · raport techniczny + executive summary · pełna anonimizacja danych