← Stack-Leitfäden

Next.js

Sicherheit für deine Next.js-App

Next.js macht es leicht, Server- und Client-Code zu mischen, und genau da schleichen sich die meisten Fehler ein: eine NEXT_PUBLIC-Variable zu viel, eine ungeschützte API route oder source maps in der Produktion. Scanne deine App und hab in 30 Sekunden Gewissheit.

Typische Risiken bei Next.js

Geheimnisse im Bundle durch NEXT_PUBLIC

Alles mit dem Präfix NEXT_PUBLIC_ landet im Browser. Ein privater Schlüssel dort ist für immer sichtbar für jeden, der die DevTools öffnet.

API routes ohne Autorisierung

Eine API route ist eine öffentliche URL. Wenn du nur den Button in der Oberfläche versteckst, aber die Session im Handler nicht prüfst, kann sie jeder direkt aufrufen.

Source maps und Header per Standard

Next.js fügt keine Sicherheits-Header für dich hinzu, und viele Builds veröffentlichen source maps, mit denen jeder deinen Code rekonstruieren kann. Beides wird in der next.config behoben.

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 productionBrowserSourceMaps aktivierst.

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.

Verwandte Checks

Besteht deine Next.js-App diese Checks?

Scanne sie kostenlos und erhalte einen Fix-Prompt für jedes Problem, das wir finden.

Meine Website kostenlos scannen