← Stack-gidsen

Next.js

Beveiliging voor je Next.js-app

Next.js maakt het makkelijk om server- en clientcode te mengen, en juist daar sluipen de meeste fouten binnen: een NEXT_PUBLIC-variabele te veel, een onbeschermde API route of source maps in productie. Scan je app en weet het zeker in 30 seconden.

Typische risico's bij Next.js

Geheimen in de bundle door NEXT_PUBLIC

Alles met het voorvoegsel NEXT_PUBLIC_ gaat naar de browser. Een privésleutel daar ligt voor altijd bloot voor iedereen die de DevTools opent.

API routes zonder autorisatie

Een API route is een publieke URL. Als je alleen de knop in de interface verbergt maar de sessie niet in de handler controleert, kan iedereen hem direct aanroepen.

Source maps en headers standaard

Next.js voegt geen beveiligingsheaders voor je toe, en veel builds publiceren source maps waarmee iedereen je code kan reconstrueren. Beide los je op in next.config.

Bijna elk gat in een met AI gebouwde Next.js-app komt uit één verwarring: niet weten welk deel van de code op de server draait en welk deel in de browser belandt. Next.js mengt beide in hetzelfde project, en Cursor, v0 of Bolt steken de grens over zonder het te zeggen. Deze gids legt dat mentale model in vijf minuten uit, loopt door wat AI-tools in Next.js verkeerd doen, geeft je een regelblok om in je project te plakken, een reviewprompt en vier controles van buitenaf. De punt-voor-puntlijst van wat je moet oplossen staat in de Next.js-checklist vóór de lancering; dit is het waarom.

Deel 1: waar alles draait

Vijf plekken, twee werelden

In de App Router kan je code op vijf plekken leven: Server Components (alleen server, bereiken nooit de browser), Client Components (gemarkeerd met "use client", in hun geheel naar de browser gestuurd), Route Handlers (app/api/.../route.ts, publieke URL's), Server Actions (functies met "use server" die de client via POST aanroept) en de middleware (proxy genoemd in de nieuwste versies), die vóór elk verzoek draait. De laatste drie plus Server Components zijn de serverwereld; Client Components zijn de browserwereld. Alles wat je in een Client Component importeert, inclusief constanten en hulpfuncties, reist mee in de bundle.

Wat de browser bereikt

  • Omgevingsvariabelen met het voorvoegsel NEXT_PUBLIC_, ingebakken bij de build. De rest bestaat alleen op de server.
  • Alle data die een Server Component als prop aan een Client Component doorgeeft. Geef je het hele user-object door, dan reist het mee met de wachtwoordhash en het e-mailadres.
  • De bestanden in de map public/, ongewijzigd.
  • De source maps, als je ze inschakelt met productionBrowserSourceMaps.

Drie mechanismen die de grensoverschrijding voorkomen

Het pakket server-only: een import 'server-only' bovenaan een bestand laat de build falen als iemand het vanuit een Client Component importeert. Gebruik het in alles wat de database aanraakt of geheimen leest. Een data-access-laag: één module die de sessiecontrole centraliseert en alleen de benodigde velden teruggeeft (een DTO), in plaats van de database vanuit elke pagina te bevragen. En de regel dat elk toegangspunt de sessie controleert: Route Handlers, Server Actions en Server Components die privédata lezen, elk afzonderlijk. De middleware vervangt die controle niet, zoals CVE-2025-29927 liet zien.

Wat Next.js niet voor je doet

Het voegt geen beveiligingsheaders toe, beperkt geen verzoeken, valideert geen Server Action-invoer en beschermt geen routes omdat ze in een map met de naam (admin) staan. Dat is allemaal jouw code of configuratie in next.config.

Deel 2: wat de AI verkeerd doet in Next.js

  • Alles met "use client". Wanneer een component state nodig heeft, markeert de AI hem als client en sleept de databasequery of de API-sleutel mee. Splits: de servercomponent haalt de data op, de clientcomponent rendert alleen.
  • Bescherming alleen in de middleware. De middleware stuurt door als er geen cookie is en de handler vertrouwt daarop. Controleer de sessie ook in de handler; dat leerde de middleware-CVE, en versies vóór 15.2.3 of 14.2.25 blijven kwetsbaar.
  • Route Handlers en Server Actions zonder authenticatie of validatie. Een Server Action is een publiek endpoint, ook zonder zichtbare URL. Controleer wie aanroept en valideer de body met een schema (zod of vergelijkbaar) voordat je iets aanraakt.
  • Geheimen met het publieke voorvoegsel. NEXT_PUBLIC_OPENAI_KEY omdat de clientcomponent hem "nodig had". De sleutel gaat in een Route Handler en de client roept de handler aan.
  • remotePatterns met een wildcard. hostname: '**' in de afbeeldingsconfiguratie maakt van je image optimizer een open proxy voor elke URL.
  • Redirects met een parameter. redirect(searchParams.next) zonder te controleren of de bestemming een intern pad is: phishing met jouw domein.
  • fetch naar gebruikers-URL's. Een handler die downloadt wat hij krijgt, kan interne diensten van je netwerk of je provider bereiken.
  • dangerouslySetInnerHTML met gebruikersinhoud. Klassieke XSS; heb je HTML nodig, saneer het dan op de server.
  • Geen headers, geen limieten. Geen CSP, geen HSTS, geen X-Frame-Options, en een endpoint dat een betaald model aanroept zonder plafond per gebruiker.

Een regelblok voor je project

Plak het in het regelbestand dat je tool leest (.cursor/rules, AGENTS.md of het equivalent), zodat het bij elke generatie geldt:

Beveiligingsregels voor dit Next.js-project:
- Componenten zijn Server Components, tenzij ze state of browserevents nodig hebben. Een Client Component importeert nooit modules die de database benaderen of process.env zonder voorvoegsel lezen.
- Elke module die de database of geheimen aanraakt, begint met import 'server-only'.
- Gebruik nooit NEXT_PUBLIC_ voor een geheime sleutel. Aanroepen naar diensten met een sleutel gaan in een Route Handler of een Server Action.
- Elke Route Handler en elke Server Action controleert de sessie en valideert de invoer met een schema voordat er iets gebeurt. De middleware telt niet als authenticatie.
- Geef Client Components alleen de velden die ze nodig hebben, nooit het hele gebruikers- of rijobject.
- Redirects accepteren alleen interne paden die met één enkele slash beginnen.
- images.remotePatterns somt specifieke domeinen op, nooit een host-wildcard.
- Gebruik geen dangerouslySetInnerHTML met gebruikersinhoud.
- next.config definieert de beveiligingsheaders en schakelt productionBrowserSourceMaps niet in.

Een reviewprompt vóór het deployen

Beoordeel de beveiliging van deze Next.js-app en verander nog niets. Antwoord met een lijst:
1. Client Components die databasemodules importeren, geheimen lezen of objecten met gevoelige velden als props ontvangen.
2. Route Handlers en Server Actions zonder sessiecontrole of zonder invoervalidatie.
3. Routes waarvan de enige bescherming de middleware is.
4. NEXT_PUBLIC_-variabelen die geheime sleutels bevatten.
5. Redirects die een door de gebruiker geleverde bestemming accepteren, fetches naar gebruikers-URL's en gebruik van dangerouslySetInnerHTML.
6. remotePatterns met een wildcard, ingeschakelde productionBrowserSourceMaps en ontbrekende beveiligingsheaders in next.config.
7. De Next.js-versie en of die ouder is dan 15.2.3 of 14.2.25.
Stel voor elk punt de exacte wijziging voor en wacht op mijn bevestiging.

Controleer het resultaat van buitenaf

Vier commando's tegen je domein. Headers en de middleware-versie:

curl -sI https://jouwdomein.com | grep -iE "strict-transport|content-security|x-frame|x-content-type"
curl -s -o /dev/null -w "%{http_code}\n" -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" https://jouwdomein.com/dashboard

Geeft het tweede 200 terug op een route die naar de login zou moeten doorsturen, dan is je versie kwetsbaar voor de middleware-CVE: update vandaag nog. Daarna source maps en de API-routes die de bundle verraadt:

curl -s https://jouwdomein.com | grep -oE '/_next/static/chunks/[^"]+\.js' | head -3
curl -s https://jouwdomein.com/_next/static/chunks/main-app-XXXX.js | grep -oE '"/api/[^"]+"' | sort -u

Vraag elke API-route op zonder sessie: het juiste antwoord is een 401 of 403. Scanaris voert deze controles en zo'n veertig andere uit in 30 seconden: het detecteert dat de app Next.js is en welke versie, test de CVE-header, zoekt geheimen in de chunks, controleert of source maps gepubliceerd zijn, beoordeelt headers, CSP, cookies, CORS en blootgestelde bestanden, en geeft je voor elke fout een fix prompt om in Cursor of v0 te plakken. Deploy je op Vercel, dan behandelt de Vercel-gids publieke previews en omgevingsvariabelen.

Veelgestelde vragen

Zijn Server Actions standaard veilig?

Ze zijn moeilijker te ontdekken dan een Route Handler, maar net zo publiek: iedereen met de action-ID kan hem aanroepen. Behandel ze als een endpoint: sessie en validatie binnen de functie.

Is de middleware genoeg om het dashboard te beschermen?

Nee. Hij is handig voor een snelle redirect, maar de echte controle hoort in elke pagina, handler en action. CVE-2025-29927 maakte het mogelijk de middleware met één header te omzeilen; ook op een gepatchte versie blijft de regel dezelfde.

Kan ik de Pages Router blijven gebruiken?

Ja, en het model is hetzelfde: getServerSideProps en pages/api zijn de server, de rest is client. Geef vanuit getServerSideProps alleen terug wat de pagina nodig heeft, want alles wat je teruggeeft wordt in de HTML geserialiseerd.

Gerelateerde checks

Doorstaat je Next.js-app deze checks?

Scan hem gratis en krijg een fixprompt voor elk probleem dat we vinden.

Mijn site gratis scannen