NEXT_PUBLIC ist kein Tresor: Warum Frontend-Umgebungsvariablen keine Secrets sind
Ein Myth-Busting-Audit für Next.js, Vite, Source Maps und Build-Artefakte: So erkennen und beheben Sie versehentlich öffentlich ausgelieferte Zugangsdaten.
Ein API-Schlüssel steht in einer .env-Datei, das Repository ist privat und im Browser-Quelltext ist nichts Auffälliges zu sehen. Also ist der Schlüssel geschützt – oder?
Nicht unbedingt.
Sobald ein Wert in Client-Code landet, muss er an den Browser ausgeliefert werden. Ob er ursprünglich aus .env, einem Secret Manager oder einer CI-Variable kam, ändert daran nichts. Minifizierung, gebündelte Dateien und schwer lesbare Variablennamen machen ihn unübersichtlich, aber nicht geheim.
Die wichtigste Regel lautet deshalb:
Alles, was der Browser für die Ausführung benötigt, kann auch der Nutzer untersuchen.
Mythos 1: „Es steht in .env, also ist es geheim“
Eine .env-Datei ist zunächst nur eine Methode, Konfiguration bereitzustellen. Sie entscheidet nicht automatisch, ob ein Wert ausschließlich auf dem Server bleibt.
Next.js bündelt Werte mit dem Präfix NEXT_PUBLIC_ beim Build in JavaScript, das an den Browser gesendet wird. Vite macht dasselbe standardmäßig mit Variablen, die mit VITE_ beginnen. Beide Dokumentationen warnen ausdrücklich davor, dort sensible Werte abzulegen.
Das Präfix bedeutet also nicht „öffentlich erreichbar, aber irgendwie geschützt“. Es bedeutet: Dieser Wert darf Teil des öffentlichen Client-Bundles werden.
Geeignete öffentliche Werte sind zum Beispiel:
- eine Analytics-Site-ID,
- eine öffentliche Sentry-DSN,
- ein Feature-Flag ohne Berechtigungswirkung,
- eine öffentliche Karten- oder Zahlungsanbieter-ID, deren Sicherheitsmodell ausdrücklich browserseitige Nutzung vorsieht,
- ein API-Basis-URL ohne Zugangsdaten.
Nicht geeignet sind:
- Datenbankpasswörter,
- private API-Schlüssel,
- Admin-Tokens,
- Service-Role-Keys,
- SMTP-Zugangsdaten,
- Webhook-Secrets,
- private Signaturschlüssel,
- Tokens mit Schreib-, Abrechnungs- oder Exportrechten.
Mythos 2: „Der Wert ist minifiziert und deshalb praktisch unsichtbar“
Minifizierung reduziert Dateigröße. Sie ist keine Zugriffskontrolle.
Ein Nutzer kann:
- JavaScript-Bundles herunterladen,
- Zeichenketten durchsuchen,
- Requests und Header in den DevTools untersuchen,
- den Laufzeitwert über Breakpoints oder Konsolenzugriff beobachten,
- Source Maps verwenden, falls sie öffentlich ausgeliefert werden,
- automatisiert nach bekannten Token-Mustern suchen.
Source Maps können die ursprüngliche Struktur von transformiertem oder minifiziertem Code rekonstruierbar machen. Sie sind für Fehlerdiagnose wertvoll, sollten aber bewusst veröffentlicht, geschützt oder an einen Fehlertracking-Dienst hochgeladen werden. Die eigentliche Sicherheitsregel bleibt unabhängig davon gleich: Ein Secret darf nicht im Client-Bundle stecken – auch dann nicht, wenn keine Source Map erreichbar ist.
Mythos 3: „Der API-Anbieter nennt es Public Key, also kann nichts passieren“
Manche browserseitigen Schlüssel sind absichtlich öffentlich. Das bedeutet jedoch nicht, dass sie ohne Begrenzung ungefährlich sind.
Entscheidend sind die tatsächlichen Rechte:
- Darf der Schlüssel nur einen öffentlichen Client identifizieren?
- Ist er auf bestimmte Domains, Ursprünge oder Apps eingeschränkt?
- Gibt es serverseitige Autorisierung für jede sensible Operation?
- Können Angreifer damit Kosten erzeugen?
- Erlaubt er das Lesen, Schreiben oder Exportieren von Daten?
- Ist Rate Limiting aktiv?
- Kann der Schlüssel unabhängig rotiert werden?
Ein Publishable Key für einen Zahlungsanbieter kann korrekt sein, solange geheime Aktionen ausschließlich serverseitig autorisiert werden. Ein „öffentlicher“ Schlüssel mit Zugriff auf Kundendaten oder Admin-Funktionen ist dagegen kein öffentliches Konfigurationsdetail, sondern ein falsch gestaltetes Berechtigungsmodell.
Sechs Red Flags im realen Projekt
1. Ein Secret wurde einfach umbenannt
Aus STRIPE_SECRET_KEY wird NEXT_PUBLIC_STRIPE_SECRET_KEY, weil der Browser sonst nicht darauf zugreifen kann. Das behebt keinen Fehler. Es verschiebt das Secret direkt in die öffentliche Auslieferung.
2. Client-Komponenten importieren Server-Konfiguration
In Fullstack-Frameworks können Dateien schnell zwischen Server- und Client-Kontext wandern. Eine Hilfsdatei, die gestern nur serverseitig genutzt wurde, kann morgen von einer Client-Komponente importiert werden. Der Build muss diese Grenze prüfen, und Reviews sollten sie sichtbar behandeln.
3. Ein Build-Artefakt wird nie auf Secrets geprüft
Secret Scanning konzentriert sich häufig auf das Repository. Ein Wert kann aber erst im CI-Build aus einer Umgebungsvariable in das Bundle geschrieben werden. Deshalb sollte zusätzlich das tatsächlich erzeugte Artefakt geprüft werden.
4. Preview-Deployments verwenden Produktionswerte
Ein Pull Request erzeugt eine öffentliche Preview, die mit produktiven API-Schlüsseln gebaut wurde. Selbst wenn die Vorschau später gelöscht wird, können Bundles, Logs oder Caches bereits kopiert worden sein.
5. Alte Bundles bleiben im CDN erreichbar
Nach der Rotation eines Schlüssels wird neu deployt, doch alte gehashte Assets bleiben lange abrufbar. Das ist für nicht geheime Konfiguration normalerweise sinnvoll. Bei einem echten Leak zeigt es jedoch: Ein neues Deployment entfernt die Exposition nicht rückwirkend.
6. Das Team „löscht“ das Secret, rotiert es aber nicht
GitHub empfiehlt bei offengelegten Zugangsdaten zuerst Widerruf oder Rotation. Das Entfernen aus Code, Git-Historie oder Build-Ausgabe reicht nicht, weil der Wert bereits kopiert worden sein kann.
Ein sinnvoller Frontend-Secret-Audit
1. Öffentliche Präfixe inventarisieren
Suchen Sie nach:
NEXT_PUBLIC_VITE_- projektspezifischen
envPrefix-Werten, - Build-Replacements über
define, - generierten Runtime-Konfigurationsdateien,
- globalen Objekten wie
window.__CONFIG__, - Inline-Skripten in HTML.
Für jeden Wert muss dokumentiert sein, warum er öffentlich sein darf.
2. Client-Bundles untersuchen
Bauen Sie die Anwendung so, wie sie in Produktion gebaut wird. Durchsuchen Sie anschließend JavaScript, CSS, HTML, Manifeste und Source Maps nach:
- bekannten Secret-Namen,
- Token-Präfixen,
- Domain- oder Account-IDs,
- privaten Endpunkten,
- langen Base64- oder Hex-Werten,
- Test- und Produktionszugängen.
Ein Repo-Scan allein reicht nicht, weil CI-Werte erst beim Build in Dateien gelangen können.
3. Netzwerkanfragen prüfen
Öffnen Sie die Website in einem frischen Browserprofil und beobachten Sie:
- Request-Header,
- Query-Parameter,
- WebSocket-Verbindungen,
- GraphQL-Payloads,
- Initialisierungsobjekte,
- Fehlerberichte und Logs.
Ein Wert muss nicht im statischen Bundle stehen, um öffentlich zu sein. Sobald der Browser ihn empfängt oder sendet, kann er untersucht werden.
4. Berechtigungen statt Namen bewerten
Der Name publicKey beweist nichts. Prüfen Sie beim Anbieter die tatsächlichen Fähigkeiten, Einschränkungen, Quoten und Rotationsmöglichkeiten.
5. Servergrenzen erzwingen
Sensible Aktionen gehören hinter eine serverseitige Route oder Funktion. Der Browser sendet die notwendigen Nutzerdaten an den eigenen Server; der Server validiert Authentifizierung, Autorisierung und Eingaben und nutzt erst dort das Secret.
6. Build und Deployment absichern
Ein belastbarer Prozess kann enthalten:
- Secret Scanning und Push Protection im Repository,
- Artefakt-Scanning nach dem Produktions-Build,
- getrennte Preview- und Produktionszugänge,
- kurzlebige oder minimal berechtigte Tokens,
- keine Secrets in Build-Logs,
- kontrollierte Source-Map-Veröffentlichung,
- dokumentierte Rotation,
- Tests, die verbotene Präfixe oder Werte im Client-Bundle erkennen.
Was tun, wenn ein Secret bereits öffentlich war?
Behandeln Sie es nicht als rein kosmetischen Codefehler.
- Secret sofort widerrufen oder rotieren.
- Berechtigungen und Logs prüfen: Wurde der Schlüssel verwendet? Gab es ungewöhnliche Requests, Exporte oder Kosten?
- Client- und Serverarchitektur korrigieren.
- Neue Zugangsdaten mit minimalen Rechten ausstellen.
- Bundles, Previews, Artefakte und Caches identifizieren.
- Repository-Historie bei Bedarf bereinigen – aber erst nach der Rotation.
- Einen automatischen Regressionstest ergänzen.
- Den Vorfall und die Ursache dokumentieren.
Bei einem produktiven Token ist die Reihenfolge entscheidend. Erst unschädlich machen, dann aufräumen.
Was Website-Pflichtencheck prüfen würde
Ein technischer Check betrachtet nicht nur .env und .gitignore. Er kann untersuchen:
- öffentliche Environment-Präfixe und Framework-Konfiguration,
- Client-/Server-Grenzen in Next.js, Vite und vergleichbaren Setups,
- tatsächlich ausgelieferte JavaScript-Bundles,
- öffentliche Source Maps,
- Preview-Deployments und Build-Artefakte,
- Netzwerkrequests und Initialisierungskonfiguration,
- Rechte und Einschränkungen browserseitiger Schlüssel,
- Secret Scanning, Push Protection und Rotation,
- Caching alter Artefakte,
- sichere serverseitige Ersatzarchitektur.
Das Ziel ist keine pauschale Warnung vor Environment-Variablen. Das Ziel ist eine klare Trennung:
Öffentliche Konfiguration darf im Browser stehen. Geheimnisse dürfen dort niemals ankommen.
Wer diese Grenze nur anhand von Dateinamen oder Präfixen beurteilt, vertraut auf Etiketten. Ein belastbarer Audit prüft, was der Browser tatsächlich erhält und was ein gefundener Wert tatsächlich darf.