Fast jedes Loch in einer mit KI gebauten Next.js-App stammt aus einer einzigen Verwechslung: nicht zu wissen, welcher Teil des Codes auf dem Server läuft und welcher im Browser landet. Next.js mischt beides im selben Projekt, und Cursor, v0 oder Bolt überschreiten die Grenze, ohne es dir zu sagen. Dieser Guide erklärt dieses Denkmodell in fünf Minuten, geht durch, was KI-Tools in Next.js falsch machen, gibt dir einen Regelblock zum Einfügen in dein Projekt, einen Review-Prompt und vier Prüfungen von außen. Die Punkt-für-Punkt-Liste dessen, was zu beheben ist, steht in der Next.js-Checkliste vor dem Launch; hier geht es um das Warum.
Teil 1: Wo was läuft
Fünf Orte, zwei Welten
Im App Router kann dein Code an fünf Orten leben: in Server Components (nur Server, erreichen nie den Browser), Client Components (mit "use client" markiert, werden komplett an den Browser geschickt), Route Handlers (app/api/.../route.ts, öffentliche URLs), Server Actions (Funktionen mit "use server", die der Client per POST aufruft) und der Middleware (in den neuesten Versionen Proxy genannt), die vor jeder Anfrage läuft. Die letzten drei plus Server Components sind die Serverwelt; Client Components sind die Browserwelt. Alles, was du in eine Client Component importierst, inklusive Konstanten und Hilfsfunktionen, wandert ins Bundle.
Was den Browser erreicht
- Umgebungsvariablen mit dem Präfix
NEXT_PUBLIC_, beim Build eingebettet. Die übrigen existieren nur auf dem Server. - Alle Daten, die eine Server Component als Prop an eine Client Component gibt. Übergibst du das ganze
user-Objekt, reist es mit Passwort-Hash und E-Mail. - Die Dateien im Ordner
public/, unverändert. - Die Source Maps, falls du sie mit
productionBrowserSourceMapsaktivierst.
Drei Mechanismen gegen das Überschreiten der Grenze
Das Paket server-only: Ein import 'server-only' am Anfang einer Datei lässt den Build scheitern, wenn jemand sie aus einer Client Component importiert. Nutz es in allem, was die Datenbank berührt oder Geheimnisse liest. Eine Datenzugriffsschicht: ein Modul, das die Sitzungsprüfung zentralisiert und nur die nötigen Felder zurückgibt (ein DTO), statt die Datenbank aus jeder Seite abzufragen. Und die Regel, dass jeder Einstiegspunkt die Sitzung prüft: Route Handlers, Server Actions und Server Components, die private Daten lesen, jede für sich. Die Middleware ersetzt diese Prüfung nicht, wie CVE-2025-29927 gezeigt hat.
Was Next.js nicht für dich tut
Es setzt keine Sicherheits-Header, begrenzt keine Anfragen, validiert keine Server-Action-Eingaben und schützt keine Routen, nur weil sie in einem Ordner namens (admin) liegen. All das ist dein Code oder Konfiguration in next.config.
Teil 2: Was die KI in Next.js falsch macht
- Alles mit "use client". Braucht eine Komponente State, markiert die KI sie als Client und zieht die Datenbankabfrage oder den API-Schlüssel mit. Teile auf: Die Server-Komponente holt die Daten, die Client-Komponente rendert nur.
- Schutz nur in der Middleware. Die Middleware leitet um, wenn kein Cookie da ist, und der Handler vertraut darauf. Prüf die Sitzung auch im Handler; das hat die Middleware-CVE gelehrt, und Versionen vor 15.2.3 oder 14.2.25 bleiben verwundbar.
- Route Handlers und Server Actions ohne Authentifizierung oder Validierung. Eine Server Action ist ein öffentlicher Endpunkt, auch ohne sichtbare URL. Prüf, wer aufruft, und validiere den Body mit einem Schema (zod oder ähnlich), bevor du irgendetwas anfasst.
- Geheimnisse mit öffentlichem Präfix.
NEXT_PUBLIC_OPENAI_KEY, weil die Client-Komponente ihn „brauchte". Der Schlüssel kommt in einen Route Handler, und der Client ruft den Handler auf. - remotePatterns mit Platzhalter.
hostname: '**'in der Bildkonfiguration macht deinen Bildoptimierer zum offenen Proxy für beliebige URLs. - Weiterleitungen per Parameter.
redirect(searchParams.next), ohne zu prüfen, dass das Ziel ein interner Pfad ist: Phishing mit deiner Domain. - fetch auf Nutzer-URLs. Ein Handler, der herunterlädt, was man ihm gibt, kann interne Dienste deines Netzes oder deines Anbieters erreichen.
- dangerouslySetInnerHTML mit Nutzerinhalt. Klassisches XSS; brauchst du HTML, bereinige es auf dem Server.
- Keine Header, keine Limits. Kein CSP, kein HSTS, kein X-Frame-Options, und ein Endpunkt, der ein kostenpflichtiges Modell ohne Deckel pro Nutzer aufruft.
Ein Regelblock für dein Projekt
Füg ihn in die Regeldatei ein, die dein Tool liest (.cursor/rules, AGENTS.md oder das Äquivalent), damit er bei jeder Generierung gilt:
Sicherheitsregeln für dieses Next.js-Projekt:
- Komponenten sind Server Components, außer sie brauchen State oder Browser-Events. Eine Client Component importiert nie Module, die auf die Datenbank zugreifen oder process.env ohne Präfix lesen.
- Jedes Modul, das die Datenbank oder Geheimnisse berührt, beginnt mit import 'server-only'.
- Nutze nie NEXT_PUBLIC_ für einen geheimen Schlüssel. Aufrufe an Dienste mit Schlüssel gehören in einen Route Handler oder eine Server Action.
- Jeder Route Handler und jede Server Action prüft die Sitzung und validiert die Eingabe mit einem Schema, bevor etwas passiert. Die Middleware zählt nicht als Authentifizierung.
- Gib Client Components nur die nötigen Felder, nie das ganze Nutzer- oder Zeilenobjekt.
- Weiterleitungen akzeptieren nur interne Pfade, die mit einem einzelnen Schrägstrich beginnen.
- images.remotePatterns listet konkrete Domains, nie einen Host-Platzhalter.
- Nutze dangerouslySetInnerHTML nicht mit Nutzerinhalt.
- next.config definiert die Sicherheits-Header und aktiviert productionBrowserSourceMaps nicht.
Ein Review-Prompt vor dem Deploy
Prüfe die Sicherheit dieser Next.js-App und ändere noch nichts. Antworte mit einer Liste:
1. Client Components, die Datenbankmodule importieren, Geheimnisse lesen oder Objekte mit sensiblen Feldern als Props erhalten.
2. Route Handlers und Server Actions ohne Sitzungsprüfung oder ohne Eingabevalidierung.
3. Routen, deren einziger Schutz die Middleware ist.
4. NEXT_PUBLIC_-Variablen, die geheime Schlüssel enthalten.
5. Weiterleitungen, die ein vom Nutzer geliefertes Ziel akzeptieren, fetch auf Nutzer-URLs und Verwendungen von dangerouslySetInnerHTML.
6. remotePatterns mit Platzhalter, aktiviertes productionBrowserSourceMaps und fehlende Sicherheits-Header in next.config.
7. Die Next.js-Version und ob sie älter als 15.2.3 oder 14.2.25 ist.
Schlag für jeden Punkt die exakte Änderung vor und warte auf meine Bestätigung.
Prüf das Ergebnis von außen
Vier Befehle gegen deine Domain. Header und die Middleware-Version:
curl -sI https://deinedomain.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://deinedomain.com/dashboard
Liefert der zweite ein 200 auf einer Route, die zum Login umleiten sollte, ist deine Version für die Middleware-CVE anfällig: Aktualisiere noch heute. Danach Source Maps und die API-Routen, die das Bundle verrät:
curl -s https://deinedomain.com | grep -oE '/_next/static/chunks/[^"]+\.js' | head -3
curl -s https://deinedomain.com/_next/static/chunks/main-app-XXXX.js | grep -oE '"/api/[^"]+"' | sort -u
Fordere jede API-Route ohne Sitzung an: Die richtige Antwort ist 401 oder 403. Scanaris führt diese Prüfungen und rund vierzig weitere in 30 Sekunden aus: Es erkennt, dass die App Next.js ist, und ihre Version, testet den CVE-Header, sucht Geheimnisse in den Chunks, prüft, ob Source Maps veröffentlicht sind, kontrolliert Header, CSP, Cookies, CORS und offengelegte Dateien und gibt dir für jeden Fehler einen Fix-Prompt zum Einfügen in Cursor oder v0. Deployst du auf Vercel, deckt der Vercel-Guide öffentliche Previews und Umgebungsvariablen ab.
Häufige Fragen
Sind Server Actions standardmäßig sicher?
Sie sind schwerer zu entdecken als ein Route Handler, aber genauso öffentlich: Jeder mit der Action-ID kann sie aufrufen. Behandle sie wie einen Endpunkt: Sitzung und Validierung innerhalb der Funktion.
Reicht die Middleware, um das Dashboard zu schützen?
Nein. Sie taugt für eine schnelle Umleitung, aber die echte Prüfung gehört in jede Seite, jeden Handler und jede Action. CVE-2025-29927 erlaubte, die Middleware mit einem Header zu umgehen; auch auf einer gepatchten Version bleibt die Regel dieselbe.
Kann ich beim Pages Router bleiben?
Ja, und das Modell ist dasselbe: getServerSideProps und pages/api sind der Server, der Rest ist Client. Gib aus getServerSideProps nur zurück, was die Seite braucht, denn alles, was du zurückgibst, wird ins HTML serialisiert.