00:00:00Diesen Monat erschien ein Bericht, für den etwa 20.000 Indie-Apps gescannt wurden. Das Ergebnis: Jede neunte
00:00:04veröffentlichte Supabase-Zugangsdaten im Frontend-Code.
00:00:08Das passierte nicht in einem Server-Log oder einem privaten Repo.
00:00:11Die Daten stecken einfach im JavaScript, das jeder Besucher herunterlädt.
00:00:14Und der Punkt ist: Diese Apps wurden nicht gehackt, sie haben das Geheimnis versehentlich mit ausgeliefert.
00:00:18Um das klarzustellen: Manche Keys sollen öffentlich sein.
00:00:21Das Problem ist die Pipeline, denn derselbe Fehler kann einen Key veröffentlichen, der niemals
00:00:25im Browser landen dürfte.
00:00:27Wir veröffentlichen ständig neue Videos.
00:00:28Abonniert also am besten den Kanal.
00:00:35Hier ist die einfache Wahrheit, an der viele scheitern.
00:00:38Wir alle wissen es eigentlich, aber wenn es schnell gehen muss, übersieht man es leicht.
00:00:41Wenn man eine Umgebungsvariable als öffentlich markiert, behandelt das Build-Tool sie so, als gehöre sie
00:00:46in den Browser, und sie landet im Client-Bundle.
00:00:50Next.js nutzt dafür “Next.public
00:00:56“public”.
00:00:57“Next” ist kein Sicherheitssiegel, sondern wortwörtlich das Versandetikett.
00:01:01Das wissen wir jetzt, aber hier kommt der Teil speziell zu Supabase.
00:01:04Manche Keys sind für die Öffentlichkeit gedacht, wie Anon- oder Publishable-Keys, andere sind privat,
00:01:10wie die Service-Role oder Secret-Keys.
00:01:12Wenn ein privater Key im Browser landet, nun ja, ihr könnt euch sicher denken, was dann passiert.
00:01:17Row-Level Security (RLS) wird euch in diesem Fall nicht retten.
00:01:20In der Supabase-Doku steht klar, dass Service-Keys die RLS umgehen können.
00:01:24Wenn dieser Key also in eurem Frontend ist, spielt eure ganze Policy-Arbeit keine Rolle mehr.
00:01:29Ich zeige euch jetzt anhand meiner Sandbox genau, wie das passiert ist.
00:01:33Zuerst habe ich eine einfache CRUD-App mit Next.js gebaut und mein Supabase-Projekt verknüpft.
00:01:38Hier ist meine .env-Datei, und hier liegt der Fehler.
00:01:41Ich habe einen Key in einer Variable mit dem Präfix “public” gespeichert.
00:01:45Dieser Key ist zwar öffentlich, soll also sichtbar sein.
00:01:48Das eigentliche Problem ist, dass über denselben Next.public-Pfad versehentlich auch ein privater Key landen kann.
00:01:53Ich habe “npm run build” ausgeführt, um die App zu bauen, und sie dann gestartet.
00:01:59Hier bin ich in Chrome.
00:02:00Ich füge mal eben ein paar Testdaten in unsere Datenbank bzw. CRUD-App ein.
00:02:05Okay, jetzt öffne ich das kompilierte JavaScript-Bundle und suche danach.
00:02:10Da ist er.
00:02:12Die URL und der Key stehen buchstäblich in der Datei, die eure Nutzer gerade in ihren
00:02:18Browser geladen haben.
00:02:19Und beachtet, was NICHT passiert ist.
00:02:21Niemand ist irgendwo eingebrochen.
00:02:22Ich habe das einfach so gefunden.
00:02:24Es wurde keine Sicherheitslücke ausgenutzt.
00:02:25Das ist bloß das Auslesen dessen, was die App bereits ins öffentliche Internet geschickt hat.
00:02:29Wenn ich es sehen kann, kann es jeder sehen.
00:02:32Dev-Tools öffnen, die JavaScript-Dateien anschauen und danach suchen.
00:02:35Mehr braucht es nicht.
00:02:36Falls euer Plan also lautet: “Es wird schon niemand nachschauen” – das Internet ist voll von Leuten und Bots,
00:02:41deren einziger Job es ist, genau nach solchen Dingen zu suchen.
00:02:44Hier ist die Lösung, die wirklich funktioniert.
00:02:47Der Browser sollte ausschließlich eure API aufrufen.
00:02:50Eure API läuft serverseitig.
00:02:52Dort gehören private Keys hin.
00:02:54Verschiebt die private Operation in eine API-Route oder Server-Funktion.
00:02:58Der Client ruft euren Endpunkt auf, dieser ruft Supabase auf. Dann baut ihr das Projekt neu und prüft
00:03:03das Bundle noch einmal.
00:03:04Wenn der Key aus dem Bundle verschwunden ist, habt ihr es gefixt. Aber hört dort noch nicht auf,
00:03:09denn ihr könnt noch mehr tun.
00:03:11Stellt sicher, dass Row-Level Security für Tabellen mit Nutzerkontakt aktiviert ist und dass eure Policies
00:03:16genau das tun, was ihr beabsichtigt.
00:03:18Investiert auch Zeit in Tests.
00:03:19Ich finde, das wird oft zu oberflächlich behandelt.
00:03:21Um zu verhindern, dass der Fehler wiederkehrt: Viele fixen das einmal und führen es später
00:03:26unter Zeitdruck wieder ein.
00:03:28Baut also Sicherheitsvorkehrungen ein. Beginnt mit Secret-Scanning in der CI, damit Builds scheitern,
00:03:34wenn ein Key auftaucht, wo er nicht hingehört.
00:03:36Führt eine PR-Regel ein, nach der alles mit “Next public” oder “Vite” standardmäßig als öffentlich gilt –
00:03:41denn das ist es ja auch.
00:03:42Und schließlich: Nutzt Key-Rotation.
00:03:43Sobald ihr auch nur den leisesten Verdacht habt, dass Keys exponiert wurden, tauscht sie aus.
00:03:47Das ist besser, als erst mal abzuwarten, was in den nächsten Tagen passiert.
00:03:50Das könnt ihr sofort ausprobieren:
00:03:52Baut eure App genau so, wie ihr sie ausliefert.
00:03:55Durchsucht den Output nach Supabase-JWTs, Service-Secrets und allem, was nach einem Token aussieht.
00:04:01Wenn ihr etwas Privates findet, betrachtet es als kompromittiert – schließlich habt ihr es ja gefunden.
00:04:05Tauscht den Key aus und stellt eure Logik auf serverseitig um.
00:04:08Wenn ihr euch nur einen Satz aus diesem Video merkt, dann diesen:
00:04:11Wenn es im Bundle ist, ist es öffentlich.
00:04:13Wir sehen uns im nächsten Video.