Sicherheitsgrundlagen
Ist deine .env-Datei öffentlich? Zwölf Pfade zum Prüfen
Ist deine .env-Datei auf deinem eigenen Webserver öffentlich? Zwölf Adressen sagen es dir, und ein Treffer heißt: alles darin ist längst öffentlich.

Kurz gesagt
- Zwei verschiedene Probleme heißen beide offengelegte .env-Datei. Hier geht es um die Datei selbst, die auf deinem Webserver liegt und von jedem heruntergeladen werden kann, der die Adresse tippt.
- Ein Host, der nur dein gebautes Frontend ausliefert, kann das nicht. Ein echter Server schon, und darum waren sieben der acht Apps, die wir gefunden haben, Replit-Apps.
- Von 30.761 laufenden Apps, bei denen unser Check eine Antwort bekam, haben 8 eine private Datei ausgehändigt. Zwölf Adressen sagen dir, ob du die neunte bist.
Jemand hat dir gesagt, deine .env-Datei sei offengelegt, und du kannst dem Satz
nicht ansehen, wie ernst das ist. Er deckt zwei völlig verschiedene Situationen
ab. Die eine ist das Werkzeug, das tut, wofür es gebaut wurde. Die andere heißt,
dass eine Datei, die du für privat gehalten hast, von deiner laufenden Seite
heruntergeladen werden kann, von jedem, seit sie dort liegt.
Hier ist der Teil, den Anleitung um Anleitung vermischt: ein Wert aus deiner
.env, der in deinem JavaScript landet, ist nicht dasselbe Ereignis wie die
.env-Datei selbst, die dein Webserver ausliefert. Das Erste ist der Build,
der tut, worum du ihn gebeten hast, als du die Variable VITE_IRGENDWAS genannt
hast. Das Zweite ist ein Ablagefehler, und es ist das, was du zuerst prüfen
solltest, weil du es von außen selbst prüfen kannst, in etwa einer Minute.
Stell dir deine laufende Seite als Ladentheke vor. Alles auf der Theke liegt dort
zum Mitnehmen: die Seite, die Bilder, das JavaScript, das Logo. Das Hinterzimmer
dahinter enthält das, was den Laden betreibt, und ein Besucher hat keinen Weg
dorthin. Ein Wert, der in dein JavaScript kompiliert wurde, ist eine Zeile im
Prospekt auf der Theke. Die .env-Datei, die unter einer öffentlichen Adresse
antwortet, ist der Ordner aus dem Hinterzimmer, der mit allem anderen auf der
Theke liegt.
Ist meine .env-Datei öffentlich?
Öffne deine laufende Seite im Browser, hänge /.env an die Adresse und drücke
Enter.
Drei Dinge können zurückkommen, und nur eines davon ist ein Problem.
Eine Seite deiner App. Die meisten Frontends beantworten jede unbekannte Adresse mit ihrer eigenen Indexseite, weil Single-Page-Apps so routen. Du bekommst deine Startseite oder deinen Nicht-gefunden-Bildschirm. Unter diesem Pfad wird nichts ausgeliefert.
Einen Fehler. Eine 403 oder eine 404 heißt, der Server wurde gefragt und hat abgelehnt. Das ist die Antwort, die du willst.
Reinen Text, mit Zeilen darin. So etwas wie SUPABASE_URL=https://… und
SUPABASE_SERVICE_ROLE_KEY=eyJ…, als Wand aus Monospace ohne jede Gestaltung.
Die Datei wird an jeden ausgehändigt, der danach fragt, und zwar seit dem Tag,
an dem sie zum ersten Mal ausgeliefert wurde.
Zwei verschiedene Dinge heißen offengelegte .env-Datei
Das eine betrifft dein Bundle. Das andere deinen Server.
| Was passiert ist | Wo der Wert liegt | Was die Lösung kostet |
|---|---|---|
Du hast eine Variable VITE_, NEXT_PUBLIC_ oder EXPO_PUBLIC_ genannt | Einkompiliert in das JavaScript, das jeder Besucher lädt | Die Arbeit auf einen Server verlegen, dann den Schlüssel tauschen. Die Datei wurde nie ausgeliefert. |
| Dein Webserver veröffentlicht dein Projektverzeichnis | In der Datei, unter https://deineseite/.env | Die Datei aus dem veröffentlichten Verzeichnis nehmen, dann alles darin tauschen. |
Die erste Zeile ist der häufige Fall und hat einen eigenen Beitrag: Das Präfix ist eine Anweisung zu veröffentlichen, und der Build hat sie befolgt. Alles Weitere hier ist die zweite Zeile.
Warum eine Lovable-App das nicht kann und eine Replit-App schon
Weil die beiden Hosts Verschiedenes veröffentlichen.
Ein Builder, der ein statisches Frontend deployt, übergibt seinem Host einen
Ordner mit gebauten Dateien: etwas HTML, etwas JavaScript, ein paar Bilder. Deine
.env wurde beim Build gelesen und liegt nicht in diesem Ordner, also gibt es
unter /.env nichts zu holen. Der Host könnte die Datei gar nicht ausliefern.
Eine Replit-App betreibt meist einen eigenen Server, in ihrem eigenen Projektverzeichnis, mit deinen Dateien neben deinem Code. Ein Server, der sein Verzeichnis ausliefern soll, liefert jede Datei darin aus, und er kann nicht wissen, dass eine davon deine Schlüssel enthält. Dasselbe gilt für alles, was du auf eine eigene Maschine deployt hast.
Die Zahlen folgen der Architektur. Von 30.761 laufenden Apps, bei denen dieser Check eine Antwort bekam, haben 8 mindestens eine private Datei ausgehändigt. Sieben der acht waren Replit-Apps, und Replit-Apps waren 3.025 der 30.761. Die achte lief unter einer eigenen Domain, was dasselbe anders sagt: Da betrieb jemand einen eigenen Server.
Das ist der seltenste Fund, den wir ausgeben, und der einzige der neun ohne harmlose Erklärung. Wenn du die breitere Lesart willst, was Replit-Apps tatsächlich ausliefern: wir haben 3.042 davon gescannt.
Die zwölf Pfade zum Prüfen
Zwölf Adressen, in der Reihenfolge, in der es sich lohnt. Hänge jede an deine Domain.
| Adresse | Was darin steht | Wenn sie mit Text antwortet |
|---|---|---|
/.env | Jeder Schlüssel, mit dem deine App gebaut wurde | Alles als öffentlich behandeln und tauschen |
/.env.local | Dasselbe, aus einem lokalen Lauf | Dasselbe |
/.env.production | Dasselbe, aus deinem Live-Deploy | Dasselbe |
/.git/config | Die Remote-Adresse deines Repositorys | Das ganze .git-Verzeichnis ist meist lesbar |
/.git/HEAD | Auf welchem Branch du bist | Dasselbe, und es ist der leiseste der zwölf |
/.aws/credentials | Langlebige Amazon-Schlüssel | Bei Amazon tauschen, dann die Abrechnung prüfen |
/database.sql | Schema und Zeilen | Jede Zeile jeder Tabelle ist öffentlich |
/dump.sql | Dasselbe | Dasselbe |
/backup.sql | Dasselbe | Dasselbe |
/config.json | Was auch immer du hineingeschrieben hast | Lies sie und sieh nach; Tokens liegen hier öfter, als man denkt |
/docker-compose.yml | Service-Definitionen, oft mit Passwörtern darin | Alles tauschen, was darin steht |
/.npmrc | Ein Registry-Token | Den Token widerrufen |
Unser eigener Scan liest alle zwölf von außen und nennt dir die, die geantwortet haben. Er dauert etwa 20 Sekunden und braucht kein Konto: scanne deine App.
Was ein Treffer wirklich bedeutet
Alles in dieser Datei ist gerade öffentlich, und zwar seit dem Tag, an dem sie zum ersten Mal ausgeliefert wurde.
Du wirst nicht herausfinden, wer sie gelesen hat. Ein Builder gibt dir kein Log über eine abgeholte Datei, in das du sehen könntest, und die Anfrage sieht aus wie jede andere Anfrage nach jeder anderen Datei. Sicher sein kannst du dir, dass es jemand versucht hat: Automatische Crawler laufen genau diese zwölf Pfade im ganzen Internet ab, ununterbrochen, und sie müssen nicht wissen, wer du bist, um deine zu finden.
Fünf der acht Apps, die wir gefunden haben, händigten eine der vier teuren
Sorten aus: .env, ein .git-Verzeichnis, .aws/credentials oder einen
.sql-Dump. Die anderen drei händigten config.json, docker-compose.yml oder
.npmrc aus. Diese drei hält man für harmlos, und genau deshalb landen Tokens
und Datenbankpasswörter darin.
Was das nicht ist, ist ein Urteil über deine App. Eine externe Prüfung liest, was deine Seite einem Fremden aushändigt, und zwölf Pfade, die mit nichts antworten, sind zwölf Pfade, die mit nichts antworten. Was sonst noch aus einer vibe-codeten App herausläuft ist die längere Liste.
Das eine, das eine Woche ruiniert: ein .git-Verzeichnis
Ein .git-Verzeichnis ist nicht eine Datei. Es ist deine ganze Geschichte.
Jeder Commit steckt darin, auch der, in dem du einen Schlüssel eingefügt hast,
und der spätere, in dem du ihn wieder herausgenommen hast. Das ist der Teil, den
Leute bei einem .git-Leck falsch verstehen: Einen Schlüssel aus dem Code von
heute zu löschen ändert nichts an der Version, in der er noch drin war, und
diese Version liegt in demselben Verzeichnis, das dein Server veröffentlicht.
Der Check liest /.git/config und /.git/HEAD, weil die beiden klein sind, ihr
Inhalt unverwechselbar ist und jede der beiden Antworten bedeutet, dass das
Verzeichnis selbst ausgeliefert wird. Von da an braucht ein Leser kein
Spezialwerkzeug. Das Format ist dokumentiert, und gewöhnliche Software klont es.
Wenn ein Schlüssel je in einem Commit war, hilft nur noch tauschen. Was zuerst kommt, hängt davon ab, ob die Kopie schon draußen ist, und diese Reihenfolge lohnt sich.
Der Dump, den jemand im Ordner liegen ließ
Drei der zwölf Pfade sind Datenbank-Dumps: /database.sql, /dump.sql und
/backup.sql.
Ein Dump ist jede Zeile jeder Tabelle in einer Datei, mit dem Schema darüber. E-Mail-Adressen, gehashte Passwörter, Bestellungen, Nachrichten, was auch immer deine App hält. Er antwortet unter einer öffentlichen Adresse aus dem langweiligsten Grund in diesem ganzen Beitrag: Jemand hat das Vernünftige getan, eine Kopie seiner Datenbank gezogen und sie in dem Projektordner gespeichert, in dem er gerade stand.
So wurde die Datei, die die Daten schützen sollte, zum schnellsten Weg, sie alle zu lesen. Wo eine Kopie liegt, ist genauso Teil der Entscheidung wie die Frage, ob du eine ziehst. Drei Wege, eine Supabase-Datenbank zu sichern nennt die Möglichkeiten, und Reeve Care bewahrt eine Kopie deiner Supabase-Datenbank außerhalb deines eigenen Servers auf, geprüft, bevor sie als Backup zählt.
Wie du es behebst, und warum die Reihenfolge zählt
Nimm die Datei aus dem, was dein Server veröffentlicht. Tausche danach jeden Schlüssel, der darin stand. In dieser Reihenfolge.
Zuerst zu tauschen fühlt sich nach der dringenden Hälfte an, und es ist die Hälfte, die die Arbeit verschwendet. Deine neuen Schlüssel landen in derselben Datei, die Datei antwortet weiter unter derselben Adresse, und du hast direkt zurück ins Leck getauscht. Nichts ist sicherer als vor zehn Minuten.
Wohin sie stattdessen gehört, je nachdem, was du betreibst:
- Ein Host mit eigenem Secrets-Speicher. Replit Secrets, die Umgebungsvariablen einer Plattform, dein Hosting-Dashboard. Der Wert wird zur Laufzeit von deinem Server gelesen und liegt nie als Datei im veröffentlichten Verzeichnis.
- Außerhalb des ausgelieferten Verzeichnisses. Wenn du die Serverkonfiguration kontrollierst, zeige auf einen Build-Ausgabeordner statt auf das Projektverzeichnis. Dann hat eine Datei im Projektverzeichnis überhaupt keine Adresse.
- Auch nicht im Repository, was eine eigene Gewohnheit ist und eine gute. Für das Problem von heute tut es nichts.
Der letzte Punkt gehört deutlich gesagt, denn .gitignore ist die Antwort, nach
der alle greifen. Sie hält die Datei aus deinem Repository heraus. Die Datei auf
deinem Server liegt dort, weil der Server in deinem Projektverzeichnis sitzt, und
git hat dazu keine Meinung.
Was du jetzt tun solltest
Was zu tun ist
- Tippe alle zwölf Adressen hinter deine eigene Domain, oder lass den kostenlosen Scan das Tippen übernehmen. Lies, was zurückkommt, nicht den Statuscode.
- Antwortet eine davon mit deinen eigenen Einstellungen, nimm die Datei aus dem Verzeichnis, das dein Server veröffentlicht, und deploye neu. Das ist der Schritt, der es stoppt.
- Tausche danach jeden Schlüssel, jedes Passwort und jeden Token, den die Datei hielt, bei jedem Anbieter. Vor dem Umzug zu tauschen schreibt die neuen Werte dorthin, wo die alten waren.
- Sieh dir danach Abrechnung und Anbieter-Logs an. Tauschen stoppt, was als Nächstes passiert, und nichts an dem, was schon passiert ist.
- War ein
.git-Verzeichnis lesbar, tausche alles, was je in einem Commit war, nicht nur das, was heute im Code steht.
Wenn du deine App lieber als Liste durchgehst: Die 10-Minuten-Sicherheitscheckliste deckt das hier zusammen mit den anderen Dingen ab, die in einer frisch gestarteten App zu schließen sind.
FAQ
Woher weiß ich, ob meine .env-Datei öffentlich ist?
Öffne deine laufende Seite im Browser, hänge /.env an die Adresse und drücke Enter. Kommt eine Seite deiner App zurück, wird unter diesem Pfad nichts ausgeliefert. Kommt reiner Text zurück, mit Zeilen wie SUPABASE_URL=https://abcdefghij.supabase.co, dann wird die Datei an jeden ausgehändigt, der danach fragt. Mach dasselbe mit /.env.local und /.env.production, denn ein Server, der eine davon ausliefert, liefert meist alle drei.
Ist eine .env-Datei in meinem Bundle dasselbe?
Nein, und die Lösung ist eine andere. Ein Wert, der in deinem JavaScript landet, ist dort, weil der Build es so machen sollte: Die Variable hieß VITE_ oder NEXT_PUBLIC_ oder EXPO_PUBLIC_. Die Datei hat deinen Rechner nie verlassen, der Wert schon. Das ist ein eigenes Problem mit einem eigenen Beitrag. Hier geht es um die Datei selbst, die dein Webserver ausliefert, und damit ist jeder Wert darin öffentlich, mit Präfix oder ohne.
Warum kann jemand meinen .git-Ordner herunterladen?
Weil dein Server auf dein Projektverzeichnis zeigt und .git ein Verzeichnis darin ist wie jedes andere. Ein Server weiß nicht, dass eines davon deine Versionsgeschichte ist. Wenn /.git/config oder /.git/HEAD mit Text antwortet, ist das ganze Verzeichnis meist lesbar, und dazu gehört jeder Commit, den du je gemacht hast, nicht nur der Code von heute.
Ich habe eine backup.sql auf meiner eigenen Seite gefunden, was jetzt?
Nimm sie aus dem Verzeichnis, das dein Server veröffentlicht, bevor du irgendetwas anderes tust, denn die Datei wird ausgehändigt, während du das hier liest. Behandle danach jedes Passwort, jeden Schlüssel und jeden Personendatensatz darin als öffentlich: Ein Dump enthält das Schema und jede Zeile jeder Tabelle. Und finde heraus, wie die Datei dorthin kam, meist ist es ein Backup im Projektordner, das nie umgezogen ist.
Soll ich zuerst Schlüssel tauschen oder die Datei löschen?
Erst die Datei verschieben, dann tauschen. Wer tauscht, während die Datei noch ausgeliefert wird, schreibt die neuen Werte in etwas, das jeder herunterladen kann, und steht danach genau dort, wo er vorher stand. Sobald unter dem Pfad nichts mehr antwortet, tausche jeden Schlüssel, den die Datei hielt, bei jedem Anbieter, und sieh dir danach Abrechnung und Logs an.