Zum Inhalt springen

Sicherheitsgrundlagen

Supabase "RLS disabled in public": was die Warnung übersieht

Supabase meldet "RLS disabled in public" als Fehler. Zur Lese-Policy, die deine Tabelle genauso offen lässt, sagt der Advisor gar nichts.

Vlad Tkachenko10 Min. Lesezeit
Ein Dashboard-Panel mit mehreren Tabellen, ein Warnzeichen neben einer davon und nichts neben den anderen.

Kurz gesagt

  • "RLS disabled in public" heißt genau eine Sache: Bei einer Tabelle in deinem public-Schema ist Row Level Security ausgeschaltet, also kann sie jeder lesen, der deine Projektadresse und deinen veröffentlichbaren Schlüssel hat.
  • Über eine Tabelle, bei der du den Schalter angemacht und danach eine Policy geschrieben hast, die alle lesen lässt, sagt die Meldung nichts. Für immer wahre Policies gibt es eine eigene Regel, und die lässt Lese-Policies absichtlich aus.
  • Räum die gemeldeten Tabellen auf und prüfe den Rest danach von außen. Der Advisor liest deine Einstellungen und fragt deine Datenbank nie, was ein Fremder tatsächlich zurückbekommt.

Du hast den Security Advisor in deinem Supabase-Dashboard geöffnet, oder jemand hat dir dessen Ausgabe hingeworfen, und da steht es in Rot: RLS disabled in public. Darunter eine Zeile pro Tabelle, in Supabases eigenen Worten:

Table public.profiles is public, but RLS has not been enabled.

Hier ist der Punkt, den Anleitung um Anleitung falsch darstellt: Diese Liste abzuarbeiten heißt nicht, dass niemand deine Daten lesen kann. Für eine Policy, die alle hereinlässt, hat der Advisor eine eigene Regel, und diese Regel überspringt genau die Form, die ein KI-Baukasten schreibt, wenn er deine App wieder zum Laufen bringt. Die Liste, die du gerade abgearbeitet hast, und die Liste der Tabellen, die ein Fremder lesen kann, sind also zwei verschiedene Listen, und eine davon steht nirgends in deinem Dashboard.

Was bedeutet "RLS disabled in public"?

Bei einer Tabelle in deinem public-Schema ist Row Level Security ausgeschaltet, und die Folge davon ist, dass jeder mit deiner Projektadresse jede Zeile darin lesen kann.

Beide Hälften davon brauchen eine Erklärung. Das public-Schema ist der Ort, an den eine Tabelle geht, wenn niemand etwas anderes sagt, und es ist der Teil deiner Datenbank, den Supabase im Web veröffentlicht: Jedes Projekt beantwortet Anfragen unter einer eigenen Adresse, und der Schlüssel, den man dafür braucht, steckt in dem Code, den deine Seite an jeden Besucher schickt. Row Level Security ist der Schalter, der entscheidet, ob deine Regeln geprüft werden, bevor Zeilen herausgegeben werden. Ist er aus, gibt es nichts zu prüfen, und deshalb lautet die Antwort immer ja.

Diese Kombination ist der Grund, warum das hier als Fehler und nicht als Warnung gemeldet wird. Der Advisor stuft seine Funde ein, und das hier ist die oberste Stufe.

Stell dir den Advisor als Prüfer mit einem Klemmbrett vor. Er liest deine Unterlagen sorgfältig und er ist gut darin. Er probiert nie die Klinke. Nichts in diesem Panel ist das Ergebnis einer Anfrage, die jemand an deine Datenbank gestellt hat.

Bricht meine App, wenn ich RLS anschalte?

Ja, sofort, und genau das ist der Schalter bei der Arbeit.

Die Behebung, die Supabase dir gibt, ist eine Zeile, und du führst sie im SQL Editor aus:

alter table public.profiles enable row level security;

Supabases eigene Dokumentation ist deutlich, was als Nächstes passiert: Mit einem veröffentlichbaren Schlüssel sind die Daten über die API nicht mehr erreichbar, solange keine Policies definiert sind. Deine Listen kommen also leer zurück, deine Bildschirme bleiben weiß, und der Fehler im Advisor wird durch einen leiseren Eintrag ersetzt, der sagt, dass die Tabelle RLS an hat, aber keine Policies.

Das ist die geschlossene Tür, bei der noch niemand auf der Liste steht. Als Nächstes fügen die meisten eine Policy hinzu, die alle lesen lässt, weil danach die Bildschirme wieder etwas anzeigen.

Der Fall, nach dem die Warnung gar nicht sucht

Eine Tabelle mit angeschalteter Row Level Security und einer Lese-Policy, deren Bedingung USING (true) lautet, gibt genau dieselben Zeilen an genau denselben Fremden heraus. Der Advisor meldet sie nicht.

Das ist kein Versehen. Für immer wahre Policies hat Supabase durchaus eine Regel, und Lese-Policies lässt sie absichtlich aus. Die eigene Beschreibung der Regel sagt das so:

SELECT policies with `USING (true)` are intentionally excluded as this
pattern is often used deliberately for public read access.

Für den allgemeinen Fall hat die Regel recht. Ein Produktkatalog, eine Liste veröffentlichter Artikel, eine Karte mit Veranstaltungsorten: Die sollen für jeden lesbar sein, und sie zu melden würde jeden Entwickler auf der Plattform darauf trainieren, das Panel zu ignorieren. Was kein Linter wissen kann, ist, ob in der Tabelle vor ihm Veranstaltungsorte oder Kunden stehen.

Und eine Policy, die alle lesen lässt, ist der schnellste Weg, eine kaputte App wieder zum Laufen zu bringen, weshalb ein KI-Baukasten danach greift. Bitte Cursor oder Lovable, die leeren Bildschirme zu reparieren, und FOR SELECT USING (true) ist eine häufige Antwort. Deine App lädt, der Fehler verschwindet, das Panel wird still, und die Tabelle ist so lesbar wie vorher.

Was der Advisor sehen kannWie er es meldetWas ein Fremder mit deinem veröffentlichbaren Schlüssel bekommt
RLS ausgeschaltetFehlerJede Zeile
RLS an, gar keine PoliciesInfoNichts
RLS an, FOR SELECT USING (true)Gar nichtsJede Zeile
RLS an, FOR ALL USING (true)WarnungJede Zeile, und er kann sie ändern
RLS an, USING (auth.uid() = user_id)Gar nichtsNur die eigenen

Die beiden Zeilen, bei denen gar nichts gemeldet wird, sind das Paar, bei dem es sich zu verweilen lohnt. Die eine ist eine Tabelle, die außerhalb deiner App niemand anfassen kann. Die andere ist eine Tabelle, die jeder lesen kann. Dein Dashboard schweigt zu beiden gleich.

Dieselben vier Zeilen, an denselben Fremden herausgegeben. Der Unterschied zwischen den Bahnen ist, zu welcher dein Dashboard überhaupt etwas sagt.

Wie diese Policy zustande kam und wodurch du sie Tabelle für Tabelle ersetzt, steht in Row Level Security ist an, die Tabelle ist trotzdem offen. Wenn dieselbe Policy auch noch erlauben soll, dass deine App speichert, ist der Fehler, den du als Nächstes triffst, new row violates row-level security policy.

Warum die Tabelle aus einer Migration nie gewarnt hat

Weil der Table Editor Row Level Security für dich anschaltet und SQL das nicht tut.

Supabase dokumentiert die Trennung unmissverständlich: Bei Tabellen, die über den Table Editor im Dashboard entstehen, ist RLS standardmäßig an, und bei Tabellen aus rohem SQL muss es ausdrücklich angeschaltet werden. Eine Tabelle, die du zusammengeklickt hast, startet geschützt. Eine Tabelle, die über eine Migrationsdatei, ein supabase db push, einen Schnipsel im SQL Editor oder eine Anweisung deines KI-Baukastens ankam, startet offen.

Der zweite Weg ist der, auf dem ein KI-Baukasten Tabellen anlegt. Er schreibt das SQL und führt es für dich aus, du hast das Häkchen also nie gesehen und nie gesehen, dass es fehlte.

Die Gewohnheit, die das schließt, ist die Zeile in der Migration direkt neben dem, was sie schützt:

create table public.profiles (
  id uuid primary key references auth.users,
  full_name text
);

alter table public.profiles enable row level security;

So prüfst du die Tabellen, die der Advisor durchgewinkt hat

Stell deiner Datenbank die Frage, die ein Fremder stellt: Schick eine Anfrage von außen, mit dem veröffentlichbaren Schlüssel, der in deiner App mitgeliefert wird, und sieh dir an, was zurückkommt.

Das ist der Unterschied, um den sich der ganze Artikel dreht. Der Advisor liest deine Konfiguration. Eine Anfrage liest deine Zeilen. Das sind zwei verschiedene Fragen, und eine Tabelle kann die erste bestehen und die zweite nicht, was eine großzügige Lese-Policy genau so tut.

Der Advisor endet bei deinen Einstellungen. Eine Anfrage von außen geht weiter bis zu den Zeilen, und deshalb können die beiden zur selben Tabelle Verschiedenes sagen.

Wir haben diese Anfrage in großem Maßstab gestellt. Zwischen dem 12. und dem 14. August 2026 haben wir neun externe Prüfungen über 30.998 laufende Apps aus Lovable, Base44, Replit, v0 und Bolt ausgeführt. Von den 3.680 Supabase-Apps, bei denen die Prüfung durchlief, hatten 2.096 mindestens eine Tabelle, die einer anonymen Anfrage Zeilen zurückgegeben hat. Das sind 57 %, und zwar als Anteil an den Apps, von denen wir eine klare Antwort bekommen haben, und nicht an allem, was wir gescannt haben. Bei den Apps aus Bolt waren es 27 von 35, eine kleine genug Stichprobe, um sie als Richtung und nicht als Quote zu lesen. Der vollständige Datensatz ist veröffentlicht, und worauf sich die 57 % beziehen geht die Zählung durch.

Du kannst dieselbe Anfrage mit einem Browser und deinem eigenen veröffentlichbaren Schlüssel selbst an eine Tabelle stellen. Wenn du das lieber nicht Tabelle für Tabelle machst: Unser kostenloser Scan fragt deine laufende App von außen und sagt dir, welche Tabellen geantwortet haben. Er dauert etwa 20 Sekunden und braucht kein Konto: App scannen.

Brauche ich ein Backup, bevor ich RLS-Policies ändere?

Für jede Tabelle, in die deine App schreibt, ja. Bei einer Tabelle, die nur gelesen wird und die du enger fasst, besteht das Risiko darin, dass deine App weiß bleibt, und nicht darin, dass Daten verschwinden.

Zwei verschiedene Dinge lohnen sich hier zu trennen, weil nur eines davon mit der Reparatur zu tun hat.

Was eine großzügige Policy bereits erlaubt hat. Wenn die Regel auf der Tabelle FOR ALL USING (true) statt FOR SELECT war, dann konnte jeder, der sie gefunden hat, Zeilen nicht nur lesen, sondern auch ändern und löschen, und sie heute enger zu fassen ändert nichts an gestern. Diese Variante taucht meistens als Support-Nachricht über Daten auf, die sich von selbst geändert haben, oder als eine Tabelle, die plötzlich leer ist.

Die Reparatur selbst. Policies über ein Dutzend Tabellen umzuschreiben ist eine Änderung an einer laufenden Datenbank, geschrieben von denselben Werkzeugen wie das Problem. Eine Migration, die eine Policy löscht und falsch neu anlegt, ist ein ganz gewöhnlicher Dienstag, und der Weg zurück ist eine Kopie davon, wie es vor einer Stunde war.

Auf einem bezahlten Supabase-Plan liegt die Kopie von letzter Nacht in der Konsole. Auf dem kostenlosen Plan gibt es nichts, worauf du zurückfallen kannst, weil der kostenlose Plan überhaupt keine automatischen Backups macht. Wenn das auf dich zutrifft, nimm eine Kopie, bevor du eine Policy anfasst: So sicherst du eine Supabase-Datenbank im kostenlosen Tarif ist die Zehn-Minuten-Fassung.

Wovon Reeve Care eine Kopie behält

Von deiner Supabase-Datenbank, nach Zeitplan kopiert, außerhalb deines Supabase-Kontos gehalten, verschlüsselt und zurückgelesen, bevor sich das Datum in deinem Dashboard bewegt. Die Dateien, die deine Nutzer hochgeladen haben, reisen mit, sobald du einen Storage-Schlüssel verbindest.

Zwei Grenzen, vorab gesagt. Backups gibt es nur für Supabase: Wenn deine Daten woanders liegen, sagen wir das, statt dir ein Abo zu verkaufen, das eine leere Kiste bewacht. Und der Storage-Schlüssel wird getrennt abgefragt, weil Supabase für Dateien keinen nur lesenden Schlüssel ausgibt, der Schlüssel für deine Uploads also auch schreiben kann. Der Schlüssel für deine Datenbank kann das nicht. Ihn zu verbinden ist optional, und die Datenbank wird so oder so gesichert.

Der Restore ist der Teil, auf den es in diesem Artikel ankommt. Eine alte Kopie über eine laufende Datenbank zu legen ist der unheimlichste Knopf im Produkt, also nimmt Care zuerst eine Kopie des aktuellen Zustands und spielt erst dann die ein, die du ausgewählt hast. Der Restore hat selbst ein Rückgängig.

Was passiert, wenn du auf Restore drückst. Der aktuelle Zustand wird herauskopiert, bevor irgendetwas eingespielt wird, also ist auch das Drücken des Knopfes umkehrbar.

Die andere Hälfte von Care ist die, auf die dieser Artikel dauernd zeigt. Eine Policy, die sich während einer Migration gelockert hat, findest du nicht durch Hinschauen, also läuft dieselbe externe Prüfung nach Zeitplan erneut und sagt dir, wenn sich die Antwort ändert. Uptime, ein Monatsbericht und der Scan gehören zum selben Abo.

Was eine Kopie nicht tut, ist deine Policies für dich zu schreiben, und kein Backup macht aus einer offenen Tabelle eine geschlossene. Diese Tabellen bleiben deine Aufgabe. Was die Kopie ändert, ist, was passiert, wenn die Reparatur schiefgeht. Was Reeve bei Supabase sichert, wie oft, und was ein Restore tut zeichnet den ganzen Zyklus, und die Tarife und ihre Preise stehen auf der Preisseite.

Was du diese Woche tun solltest

Was zu tun ist

  • Räum zuerst die Einträge "RLS disabled in public" ab. Das sind die Tabellen, bei denen überhaupt nichts geprüft wird, und die Behebung ist je eine alter table-Zeile.
  • Öffne dann Authentication → Policies und lies die Bedingung jeder Policy, die übrig geblieben ist. Ein USING (true) auf einer Lese-Policy ist für den Advisor unsichtbar und für einen Fremden weit offen.
  • Entscheide Tabelle für Tabelle, ob es dir recht wäre, ihren Inhalt auf einer Seite zu veröffentlichen. Das ist die Frage, die der Linter dir nicht beantworten kann, und bei einer großzügigen Lese-Policy die einzige, die zählt.
  • Schreib alter table ... enable row level security; in jede Migration, die eine Tabelle anlegt, und lass den Advisor nach jeder neu laufen. Die Voreinstellung im Dashboard gilt nur für Tabellen, die du klickst.
  • Nimm eine Kopie deiner Datenbank, bevor du Policies an einer laufenden Tabelle umschreibst, und schau nach, auf welchem Supabase-Plan du bist, damit du weißt, ob du schon eine hast.

Fang mit der Tabelle an, die dir als öffentliche Seite am peinlichsten wäre. Die Sicherheits-Checkliste in 10 Minuten deckt das zusammen mit dem Rest ab, was bei einer frisch veröffentlichten App zu prüfen ist, und der Supabase-Sicherheitsleitfaden geht durch, was sonst noch offen bleibt.

FAQ

Ist "RLS disabled in public" ein Fehler oder eine Warnung?

Ein Fehler, und zwar auf der höchsten Stufe, die der Advisor kennt. Der Eintrag lautet "Table public.<name> is public, but RLS has not been enabled." Die beiden verwandten Einträge sind leiser: Eine Tabelle mit angeschaltetem Schalter und ganz ohne Policies wird als INFO gemeldet, und eine Policy mit einer immer wahren Bedingung als WARN. Diese Stufen sagen, wie sicher sich der Linter bei dem ist, was er in deiner Konfiguration sehen kann, und nicht, wie groß dein Problem ist.

Bricht meine App, wenn ich Row Level Security anschalte?

Sofort, ja, und genau das ist der Schalter bei der Arbeit. Supabase dokumentiert, dass Daten mit einem veröffentlichbaren Schlüssel nicht mehr über die API erreichbar sind, solange keine Policies existieren. In dem Moment, in dem du die Zeile ausführst, kommen deine Listen also leer zurück und deine Bildschirme bleiben weiß. Die App kommt zurück, sobald du eine Policy hinzufügst, die beschreibt, wer welche Zeilen sehen darf. Der Fehler, den es zu vermeiden gilt: eine, die alle hereinlässt, denn diese Variante bringt die App genauso zum Laufen.

Ich habe RLS angeschaltet und jetzt lädt nichts mehr. Was ist passiert?

Nichts ist kaputt. Mit angeschalteter Row Level Security und ohne geschriebene Policies weist Postgres (die Datenbank-Engine unter Supabase) jede Anfrage ab, auch die aus deiner eigenen App, und der Advisor tauscht seinen Fehler gegen einen INFO-Eintrag, der sagt, dass die Tabelle RLS an hat, aber keine Policies. Schreib eine Policy für die Zeilen, die deine App zeigen soll, und fang mit der an, die den angemeldeten Besucher mit der Besitzerspalte auf der Zeile vergleicht.

Meine Tabelle wird nicht gemeldet, aber jeder kann sie lesen. Wieso?

Höchstwahrscheinlich, weil Row Level Security an ist und es eine Lese-Policy mit der Bedingung USING (true) gibt. Für immer wahre Policies hat der Advisor durchaus eine Regel, und die lässt Lese-Policies absichtlich aus, weil öffentlicher Lesezugriff für einen Produktkatalog oder eine Liste veröffentlichter Artikel eine vernünftige Sache ist. Nichts in deinem Dashboard weiß, ob in deiner Tabelle Veranstaltungsorte oder Kunden stehen, also ist die Prüfung deine Sache.

Brauche ich Row Level Security, wenn meine App nur mit meinem eigenen Server spricht?

Wenn der Browser wirklich nie mit Supabase spricht und kein veröffentlichbarer Schlüssel in dem Code steht, den deine Seite an Besucher ausliefert, dann ist die Data API kein Weg hinein und Policies sind nicht das, was diese Tabellen schützt. In einer App aus Lovable, Bolt oder v0 ist das selten, weil diese Baukästen den Browser standardmäßig direkt an Supabase hängen. Öffne deine eigene Seite, schau nach, ob Projekt-URL und veröffentlichbarer Schlüssel darin stehen, und lass diese Antwort entscheiden.

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.