Zum Inhalt springen

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.

Vlad Tkachenko9 Min. Lesezeit
Vier versiegelte Ordner hinter einer Mauer, davor ein offener Ordner mit sichtbaren Zeilen unter einem gelben Balken.

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 istWo der Wert liegtWas die Lösung kostet
Du hast eine Variable VITE_, NEXT_PUBLIC_ oder EXPO_PUBLIC_ genanntEinkompiliert in das JavaScript, das jeder Besucher lädtDie Arbeit auf einen Server verlegen, dann den Schlüssel tauschen. Die Datei wurde nie ausgeliefert.
Dein Webserver veröffentlicht dein ProjektverzeichnisIn der Datei, unter https://deineseite/.envDie 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.

Links: Der Build hat einen Wert in dein JavaScript kopiert, weil das Präfix es so wollte. Rechts: Der Server händigt die Datei aus, und die Präfixe spielen dabei keine Rolle.

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.

AdresseWas darin stehtWenn sie mit Text antwortet
/.envJeder Schlüssel, mit dem deine App gebaut wurdeAlles als öffentlich behandeln und tauschen
/.env.localDasselbe, aus einem lokalen LaufDasselbe
/.env.productionDasselbe, aus deinem Live-DeployDasselbe
/.git/configDie Remote-Adresse deines RepositorysDas ganze .git-Verzeichnis ist meist lesbar
/.git/HEADAuf welchem Branch du bistDasselbe, und es ist der leiseste der zwölf
/.aws/credentialsLanglebige Amazon-SchlüsselBei Amazon tauschen, dann die Abrechnung prüfen
/database.sqlSchema und ZeilenJede Zeile jeder Tabelle ist öffentlich
/dump.sqlDasselbeDasselbe
/backup.sqlDasselbeDasselbe
/config.jsonWas auch immer du hineingeschrieben hastLies sie und sieh nach; Tokens liegen hier öfter, als man denkt
/docker-compose.ymlService-Definitionen, oft mit Passwörtern darinAlles tauschen, was darin steht
/.npmrcEin Registry-TokenDen 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.

Zuerst tauschen, und der neue Schlüssel landet in der Datei, die weiter ausgehändigt wird. Zuerst verschieben, und es bleibt nichts mehr zum Aushändigen.

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.

Geschrieben von

Vlad Tkachenko

Gründer, Reeve

Ich schaue mir den ganzen Tag Apps an, die mit Lovable, Bolt, v0, Cursor und Replit gebaut wurden, und die kurze Liste von Fehlern, die darin immer wieder auftauchen.

Mehr über den Autor

Weiterlesen

Alle Artikel

Unsicher, wie es um deine eigene App steht?

Starte einen kostenlosen Scan und erhalte in rund 20 Sekunden eine verständliche Note von A bis F. Ohne Konto, ohne Karte.

App kostenlos scannen

Automatisierte externe Prüfung, kein vollständiges Audit. Das Fehlen von Funden ist keine Garantie für Sicherheit.