Zum Inhalt springen

Sicherheitsgrundlagen

Geleakten Supabase-service_role-Schlüssel richtig tauschen

Supabase sagt: erst das Leck schließen. Andere Anleitungen: sofort tauschen. Was richtig ist, hängt davon ab, wohin dein service_role-Schlüssel gelangt ist.

Vlad Tkachenko9 Min. Lesezeit
Ein Vorhängeschloss und daneben zwei gleich große, identische Schlüssel, einer hell und einer verblasst.

Kurz gesagt

  • Steckt dein Supabase-service_role-Schlüssel im JavaScript, das ein Browser lädt, tausche ihn aus, bevor du irgendetwas reparierst. Die Kopie, die schon draußen ist, läuft nicht ab.
  • Ist er nur in ein privates Repo oder ein Log gelangt, schließe zuerst die Quelle. Sonst folgt dein neuer Schlüssel dem alten beim nächsten Deploy nach draußen.
  • Ein Legacy-service_role-Schlüssel lässt sich gar nicht tauschen. Ihn ungültig zu machen heißt: neue sb_secret_-Schlüssel anlegen, die App darauf umziehen und das alte Paar mit einem Schalter deaktivieren, der beide betrifft.

Jemand hat dir gesagt, dein Supabase-service_role-Schlüssel liege in deiner App, wo ihn jeder lesen kann. Bevor du irgendetwas änderst, vergewissere dich, dass es wirklich dieser Schlüssel ist: unser kostenloser Scan liest deine Live-Seite so, wie ein Fremder sie liest, und sagt dir, welchen Supabase-Schlüssel er dort tatsächlich sieht. Ohne Konto und ohne Installation.

Der Rat, der auf so einen Fund folgt, ist fast immer ein Wort: tauschen. Dann suchst du, wie das geht, und die Quellen widersprechen sich. Supabases eigene Dokumentation beginnt ihre Rotationsanleitung damit, zuerst die Ursache des Lecks zu beheben. Ein halbes Dutzend Anleitungen von Dritten sagt, du sollst sofort tauschen und hinterher Fragen stellen.

Hier ist der Punkt, den keine von beiden aufschreibt: Beide haben recht, für unterschiedliche Situationen. Und die Frage, die sie trennt, ist, wohin der Schlüssel gelangt ist.

Eines vorweg. Den Schlüssel aus deinem Code zu löschen nimmt deine eigene Kopie vom Bund. Ihn zu tauschen wechselt das Schloss. Nur das Zweite erreicht die Kopien, die andere längst haben.

Ist es wirklich der service_role-Schlüssel?

In einem neueren Projekt beantworten das die ersten Zeichen. sb_secret_ am Anfang ist der geheime. sb_publishable_ ist der Schlüssel, der in deine App gehört, und ihn dort zu finden ist überhaupt kein Fund.

Vergewissere dich, bevor du etwas Einschneidendes tust, denn der Schlüssel, den man in einer vibe-codeten App findet, ist meistens der, der dort hingehört.

Ältere Projekte vergeben ein anderes Paar, anon und service_role, und die beiden sehen fast gleich aus: gleiches Format, gleiche Länge, kein Präfix zum Lesen. Der mittlere Abschnitt eines solchen Schlüssels ist gar nicht verschlüsselt. Er dekodiert zu ein paar Zeilen Klartext, und eine davon nennt die Rolle. Welche API-Schlüssel im Frontend sicher sind geht beide Prüfungen durch.

Der Grund, sicherzugehen: Das ist seltener, als die Warnungen vermuten lassen. Wir haben 30.998 aktive vibe-codete Apps im August 2026 gescannt und in 3 davon einen ausgelieferten service_role-Schlüssel gefunden. Ein Google-API-Schlüssel, der meist harmlos und oft auf eine Domain beschränkt ist, tauchte in 1.142 auf. Die vollständige Zählung steht hier.

Erst tauschen oder erst das Leck schließen?

Es hängt davon ab, ob der Schlüssel schon öffentlich ist, und die Linie zwischen beiden Fällen ist klar.

Steckt der Schlüssel im JavaScript, das deine Besucher herunterladen, ist er jetzt öffentlich. Jeder, der deine Seite geladen hat, während er live war, hat eine Kopie, und jeder automatische Crawler, der genau nach dieser Zeichenfolge gesucht hat, ebenfalls. Nichts, was du in deinem Code änderst, erreicht diese Kopien. Erst tauschen, dann die Quelle schließen.

Ist er nur in ein privates Repository, eine Logdatei, eine CI-Variable oder einen Chat gelangt, ist die Offenlegung begrenzt. Tauschst du hier zuerst, machst du es zweimal, denn der nächste Deploy schiebt den alten Wert wieder aus dem heraus, was ihn erzeugt hat, und dein neuer Schlüssel folgt dem alten. Zuerst die Quelle schließen, dann tauschen.

Supabases Anleitung ist für den zweiten Fall geschrieben. Die dringlichen Anleitungen sind für den ersten. Wenn du das hier liest, weil ein Scanner den Schlüssel auf deiner Live-URL gefunden hat, bist du im ersten Fall.

Dieselben zwei Aufgaben, umgekehrte Reihenfolge. Entschieden wird es davon, ob die geleakte Kopie schon draußen in der Welt ist.

So tauschst du einen geheimen Supabase-Schlüssel

Hat dein Projekt sb_secret_-Schlüssel, ist das eine Sache für das Dashboard, ohne Ausfallzeit.

Ein Projekt kann mehrere geheime Schlüssel gleichzeitig halten, jeden mit eigenem Namen, und genau das macht es sicher: Du legst den neuen an, bevor du den alten wegnimmst, also ist dazwischen nichts kaputt.

  1. Öffne dein Projekt, geh zu Settings → API Keys und lege einen neuen geheimen Schlüssel an.
  2. Setze ihn überall dort ein, wo der alte benutzt wurde, und das sollte alles auf einem Server sein: Edge Functions, Webhooks, geplante Jobs, ein eigenes Backend. Wo diese Werte liegen, falls du diese Seite noch nie geöffnet hast.
  3. Deploye, dann klick dich durch die Stellen deiner App, die Daten lesen und schreiben. Ein falscher geheimer Schlüssel scheitert laut und sofort, und das ist der gute Fall.
  4. Erst danach löschst du den kompromittierten Schlüssel.

Schritt 4 ist endgültig. Supabase löscht einen geheimen Schlüssel wirklich: kein Rückgängig, kein Wiedereinschalten, keine geparkte Kopie, die du zurückholen kannst. Deshalb kommt er zuletzt.

Das Löschen eines geheimen Schlüssels meldet deine Nutzer nicht ab. Ein API-Schlüssel sagt, welche Anwendung deine Datenbank anspricht, und das Sitzungstoken eines Besuchers sagt, wer dieser Nutzer ist. Das Zweite wird gegen den Signaturschlüssel deines Projekts geprüft, und das ist ein eigener Wert, den du nicht angefasst hast.

Warum ein alter Supabase-service_role-Schlüssel nicht tauschbar ist

Weil es dafür keinen Knopf gibt. Der Troubleshooting-Hinweis von Supabase sagt, dass ein direktes Rotieren der alten anon-, service_role- und JWT-Secrets nicht mehr unterstützt wird, und verweist stattdessen auf die neuen Schlüssel.

Einen geleakten alten service_role-Schlüssel ungültig zu machen ist also eine Migration und keine Rotation:

  1. Lege die neuen Schlüssel an. Das ergänzt sb_publishable_ und sb_secret_ neben dem alten Paar. Beide Systeme laufen gleichzeitig, und nichts geht kaputt.
  2. Zieh dein Frontend auf den öffentlichen Schlüssel um. In Lovable oder Bolt ist das meist ein Wert in deinen Projekteinstellungen und keine Codezeile.
  3. Zieh jede serverseitige Verwendung auf den geheimen Schlüssel um.
  4. Deaktiviere die alten Schlüssel in deinen Projekteinstellungen.

Schritt 4 ist ein Schalter, und er betrifft beide alten Schlüssel. anon und service_role sind JWTs, die mit demselben Secret signiert sind. Der Schalter, der einen widerruft, widerruft also auch den anderen, und ein Frontend, das noch den alten anon-Schlüssel trägt, hört in dem Moment auf zu funktionieren, in dem du ihn umlegst. Deshalb kommt Schritt 2 davor.

Die alten Schlüssel zu deaktivieren ist ein Bedienelement für beide. Der anon-Schlüssel, den deine App benutzt, geht mit dem service_role-Schlüssel raus, den du ungültig machen willst.

Das Deaktivieren lässt sich rückgängig machen, und das ist hier die einzige gute Nachricht: Falls doch noch etwas einen alten Schlüssel benutzt, schaltest du sie wieder ein, während du es reparierst. Was die Migration umfasst steht ausführlicher dort.

Woher der Schlüssel geleakt ist, und wie du das schließt

Drei Ursachen erklären fast alles davon, und alle drei liegen in deinem eigenen Projekt.

Ein VITE_- oder NEXT_PUBLIC_-Präfix. Das ist keine Sicherheitseinstellung, die jemand vergessen hat einzuschalten. Es ist eine Anweisung an den Build: Leg diesen Wert ins Bundle. Eine Variable namens VITE_SUPABASE_SERVICE_ROLE_KEY wurde absichtlich in dein JavaScript kompiliert, von einem Werkzeug, das genau das getan hat, was man ihm gesagt hat.

Ein Wert, direkt in eine Komponente eingefügt. Kein Präfix im Spiel und keine .env-Datei, nur der Schlüssel in einer Codezeile, weil das der schnellste Weg war, eine Query etwas zurückgeben zu lassen.

Arbeit, die auf einen Server gehört. Ein Konto löschen, in eine Tabelle schreiben, in die deine Nutzer nicht schreiben dürfen, Zeilen über alle hinweg lesen. Zu dem Schlüssel wurde gegriffen, weil der Browser die Aufgabe nicht erledigen konnte, und die Lösung ist, die Aufgabe zu verlagern: eine Edge Function, eine Serverless Route, irgendetwas, das deine Besucher nicht herunterladen.

Woher weiß ich, ob jemand ihn benutzt hat?

Sicher wissen kannst du es meistens nicht. Was du tun kannst, ist das Zeitfenster einzugrenzen und hineinzuschauen.

Das Fenster öffnet sich mit dem Deploy, der den Schlüssel zuerst ausgeliefert hat, und schließt sich, als du ihn deaktiviert hast. Dein Supabase-Projekt führt Logs für die API und für die Datenbank, und auf diesen Zeitraum filterst du sie. Was du suchst, sind Lese- und Schreibzugriffe, die du nicht zuordnen kannst: Anfragen an Tabellen, die deine App nie anfasst, Verkehr zu Stunden, in denen niemand die App benutzt hat, Löschungen, die niemand gemacht hat.

Prüf danach die Daten selbst. Zeilenzahlen gegen das, was du erwartest, deine eigenen Kontodatensätze, alles mit einem Zeitstempel, der sich bewegt hat, während niemand gearbeitet hat. Eine Supportnachricht über Daten, die sich von selbst geändert haben, ist der Weg, auf dem die meisten dieser Fälle tatsächlich entdeckt werden, und sie kommt Wochen später.

Ein service_role-Schlüssel erreicht weder deinen Zahlungsanbieter noch deinen Mailversand. Trotzdem lohnt es sich zu wissen, ob er der einzige Schlüssel in deinem Bundle war, denn die, die Geld ausgeben, leaken auf demselben Weg: was 30.998 Apps ausgeliefert haben.

Prüf deine Live-App, bevor du sie für erledigt hältst

Lade deine Seite in einem privaten Fenster, öffne das JavaScript, das dein Browser heruntergeladen hat, und such darin nach dem alten Wert. Such dann nach dem neuen, der dort ebenfalls nicht stehen sollte.

Zwei Dinge machen diese Handprüfung sinnvoll. Ein Build kann nach dem Deploy noch eine Weile ein gecachtes Bundle ausliefern, die Datei, die ein Besucher bekommt, ist also nicht immer die, die du gerade gebaut hast. Und ein Schlüssel kann an mehr als einer Stelle liegen: ein zweiter Einstiegspunkt, ein Service Worker, ein alter Build, der noch unter einem Pfad ausgeliefert wird, auf den niemand verlinkt.

Unser kostenloser Scan erledigt diesen Teil von außen, auf der URL, die deine Besucher tatsächlich benutzen, und er dekodiert einen Supabase-Schlüssel weit genug, um die Rolle zu lesen. Ein öffentlicher Schlüssel kommt also mit einem Haken zurück. Das dauert etwa zwanzig Sekunden und braucht kein Konto: scanne deine App.

Was jetzt zu tun ist

Was zu tun ist

  • Bestätige zuerst den Schlüssel. sb_secret_ vorn, oder "role": "service_role" in einem älteren. Ein öffentlicher Schlüssel in deinem Frontend ist kein Fund.
  • Steckt er im Bundle, das deine Besucher herunterladen, tausche ihn, bevor du Code änderst. Deine App zu bearbeiten erreicht die Kopien nicht, die schon draußen sind.
  • Ist er nur in ein Repo, ein Log oder einen Chat geleakt, schließe zuerst die Quelle, damit dein nächster Deploy den neuen Schlüssel nicht gleich wieder hinausschiebt.
  • In einem neueren Projekt: einen zweiten geheimen Schlüssel anlegen, deinen Servercode darauf umziehen, deployen, dann den alten löschen. Das Löschen ist endgültig.
  • In einem Legacy-Projekt gibt es keinen Tausch-Knopf. Lege die neuen Schlüssel an, zieh dein Frontend und deinen Server darauf um, dann deaktiviere das alte Paar mit dem einen Schalter, der beide betrifft.
  • Durchsuche danach dein Live-Bundle. Ein gecachter Build kann den alten Wert nach dem Deploy noch eine Weile ausliefern.

Ein service_role-Schlüssel liest und schreibt jede Zeile in deiner Datenbank. Die schlimmste Version dieser Geschichte endet mit einer leeren Tabelle, und ob diese Zeilen zurückkommen, hängt davon ab, was du gesichert hattest, bevor das alles passiert ist.

FAQ

Geht meine laufende App kaputt, wenn ich den Schlüssel tausche?

Nicht, wenn du die Reihenfolge einhältst. In einem Projekt mit den neuen Schlüsseln legst du einen zweiten geheimen Schlüssel an, ziehst deinen Servercode darauf um, deployst und löschst erst dann den alten. So gibt es keinen Moment ohne funktionierenden Schlüssel. In einem Legacy-Projekt liegt das Risiko woanders: Das Deaktivieren des alten Paares deaktiviert auch den anon-Schlüssel, den dein Frontend benutzt. Deine App fällt also aus, wenn sie beim Umlegen des Schalters nicht schon auf dem öffentlichen Schlüssel läuft.

Kann ich die alten anon- und service_role-Schlüssel tauschen?

Nein. Die Troubleshooting-Dokumentation von Supabase schreibt, dass ein direktes Rotieren der alten anon-, service_role- und JWT-Secrets nicht mehr unterstützt wird. Einen geleakten Legacy-Schlüssel ungültig zu machen heißt: die neuen sb_publishable_- und sb_secret_-Schlüssel anlegen, deine App und deinen Servercode darauf umziehen und danach das alte Paar in den Projekteinstellungen deaktivieren.

Soll ich den alten Schlüssel löschen oder deaktivieren?

Du hast keine Wahl, weil sich die beiden Schlüsselsysteme unterschiedlich verhalten. Ein neuerer sb_secret_-Schlüssel wird gelöscht, endgültig, ohne Weg zurück. Das alte Paar wird stattdessen deaktiviert, und das lässt sich rückgängig machen: Falls doch noch etwas einen alten Schlüssel benutzt, schaltest du sie wieder ein, während du es reparierst.

Woher weiß ich, ob jemand ihn benutzt hat?

Sicher wissen kannst du es meistens nicht. Grenze stattdessen ein Zeitfenster ein: Der Schlüssel war ab dem Deploy nutzbar, der ihn zuerst ausgeliefert hat, bis zu dem Moment, in dem du ihn deaktiviert hast. Filtere die API- und Datenbank-Logs deines Supabase-Projekts auf diesen Zeitraum und suche nach Lese- und Schreibzugriffen, die du nicht zuordnen kannst. Prüfe danach deine Daten auf Zeilenzahlen und Zeitstempel, die zu nichts passen, was du getan hast.

Muss ich meinen anon-Schlüssel auch tauschen?

In einem Legacy-Projekt hast du keine Wahl: Das Deaktivieren des alten Paares nimmt beide Schlüssel auf einmal, dein Frontend braucht also vorher den neuen öffentlichen Schlüssel. In einem neueren Projekt ist der öffentliche Schlüssel für die Öffentlichkeit gedacht, und es gibt keinen Grund, ihn anzufassen. Prüfenswert ist in beiden Fällen Row Level Security, denn das ist das Einzige, was zwischen einem öffentlichen Schlüssel und deiner ganzen Datenbank steht.

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.