Deine App fühlt sich sicher an, weil Nutzer sich einloggen müssen — aber ohne supabase rls kann jeder mit dem Anon-Key jede Zeile in deinen öffentlichen Tabellen lesen.
Preview-Speichern funktioniert. Das Dashboard zeigt Daten. Du shippst in Produktion und nimmst an, die Login-Wand schützt Kundendaten. Tut sie nicht. Der Supabase-Client im Browser bringt einen öffentlichen Anon-Key mit. Ohne Row Level Security reicht dieser Key, um Tabellen über die REST-API abzufragen. Security-Forscher und Scraper brauchen deine UI nicht.
Deine Datenbank ist standardmäßig öffentlich
Supabase erstellt Postgres-Tabellen mit deaktiviertem Row Level Security, bis du es einschaltest. Der Anon-Key ist für Browser-Nutzung gedacht. Er ist kein Passwort. Er identifiziert dein Projekt und wendet an, welche Policies du definiert hast. Keine Policies bedeutet offenen Zugang innerhalb der Table Grants, die Supabase für die anon-Rolle setzt.
Was Angreifer oder neugierige Besucher ohne Login tun können:
- Jede Zeile in exponierten Tabellen per direktem REST-Call listen
- Spam-Zeilen einfügen, wenn INSERT erlaubt ist und keine Policy blockiert
- Daten updaten oder löschen, wenn Policies und Grants es erlauben
- Felder herunterladen, die du hinter React-State versteckt dachtest
Deine React-App holt nur Zeilen für den eingeloggten Nutzer, weil du die Query so geschrieben hast. Die API erzwingt diesen Filter erst, wenn RLS ihn serverseitig ergänzt. Jeder kann denselben Endpoint mit anderen Parametern wiederholen.
KI-Coding-Tools generieren supabase.from('notes').select('*') und Auth-Screens. Sie überspringen den SQL-Policy-Schritt, weil er in der UI unsichtbar ist. Die Lücke entdeckst du, wenn jemand deine Projekt-URL in ein Skript pastet.
Row Level Security ist auf deinen Tabellen noch aus
RLS aktivieren ist ein Schalter pro Tabelle. Nicht global. Du kannst profiles sperren und messages aus Versehen offen lassen. Der Table Editor zeigt ein Schild-Symbol, wenn RLS an ist.
Prüfen im Supabase-Dashboard:
- Table Editor öffnen und jede Nutzerdaten-Tabelle auswählen.
- RLS enabled im Tabellenkopf suchen oder Authentication → Policies öffnen.
- Jede Tabelle ohne Schild ist für den Anon-Key öffentlich — innerhalb der Postgres-Grants.
Oder im SQL-Editor ausführen:
select tablename, rowsecurity
from pg_tables
where schemaname = 'public';
rowsecurity = false bedeutet: Jeder mit dem Anon-Key kann Zeilen zugreifen, soweit die Rollen-Grants reichen. RLS aktivieren ohne Policies blockiert alles — auch deine App — bis du Allow-Regeln hinzufügst.
Login allein schaltet das nicht um. Supabase auth verschafft Nutzern eine Session. RLS entscheidet, welche Zeilen diese Session anfassen darf. Beides muss für Produktion konfiguriert sein.
Insert-Policy schreiben, damit Speichern wieder geht
Nach RLS-Aktivierung scheitern Inserts oft mit new row violates row-level security policy. Das ist erwartet. Default Deny ist die sichere Haltung. Du musst Policies für SELECT, INSERT, UPDATE und DELETE hinzufügen, wo nötig.
Typisches Muster für nutzereigene Zeilen:
alter table public.notes enable row level security;
create policy "Users read own notes"
on public.notes for select
using (auth.uid() = user_id);
create policy "Users insert own notes"
on public.notes for insert
with check (auth.uid() = user_id);
Häufige Fehler nach RLS-Aktivierung:
- INSERT-Policy fehlt — Speichern scheitert still in der UI
user_idbeim Insert nicht gesetzt — Policy-Check scheitert- Policy referenziert
auth.uid(), Client nutzt aber anon-Rolle ohne Login - Nur SELECT-Policy — Lesen geht, Schreiben nicht
- Service-Role-Key im Browser — umgeht RLS und leakt God-Mode-Zugang
Teste jede Policy als authentifizierter Nutzer im SQL-Editor oder per API mit echtem Session-JWT — nicht mit der Service Role. Preview nutzt manchmal Service Role im Server-Code, Produktion Anon im Client — Verhalten divergiert wie bei Auth-URLs.
Warum KI supabase rls überspringt
Policies sind SQL im Dashboard, nicht JSX. Das Modell optimiert für sichtbare Features: Formulare, Listen, Auth-Buttons. RLS taucht nicht im Component Tree auf. Generatoren pasten manchmal den Anon-Key ins Frontend und nennen den Job erledigt.
Supabase warnt in den Docs, dass RLS deine Verantwortung ist. Die Warnung verpasst man leicht, wenn die Preview schon „funktioniert“. Behandle Policies als Teil des Schemas — in Migrationen eingecheckt, vor jedem Deploy geprüft.
supabase rls aktivieren, bevor die Live-URL öffentlich ist
Tabelle für Tabelle vorgehen. Starte mit Tabellen, die PII, Billing-Status oder Nutzerinhalte halten.
- Jede
public-Tabelle inventarisieren, die deine App liest oder schreibt. - RLS auf jeder Tabelle aktivieren, die nicht weltlesbar sein soll.
- SELECT-Policies scoped auf
auth.uid()oder Membership-Regeln. - INSERT-Policies mit
with check, damit Nutzer keine Zeilen anderen zuweisen. - UPDATE- und DELETE-Policies, wenn deine App sie braucht.
- Prüfen, dass der Anon-Key ausgeloggt keine Zeilen listen kann (curl oder REST-Client).
- App eingeloggt testen und normale Flows bestätigen.
Policies in Migrationsdateien unter Versionskontrolle shippen — nicht nur als manuelle Dashboard-Klicks. Staging und Produktion bleiben dann aligned.
RLS ist eine von fünf Produktions-Einstellungen, die KI-Editoren selten abfragen. Auth-URLs, Stripe-Webhooks, Env-Vars und TLS scheitern am selben Launch-Wochenende. Die lovable deploy-Checkliste deckt den Rest ab, damit du nicht Leak für Leak überrascht wirst.
Pre-Launch-Checkliste
- RLS auf jeder Tabelle mit Nutzer- oder privaten Daten aktiviert
- SELECT-, INSERT-, UPDATE-, DELETE-Policies passen zum tatsächlichen App-Verhalten
- Ausgeloggte Requests mit Anon-Key liefern leer oder 401, nicht volle Tabellen
- Service-Role-Key nur auf dem Server, nie in Frontend-Bundles
- Policies gegen Produktionsprojekt getestet, nicht nur gegen Dev-Duplikat
- Migrationen committed, damit der nächste Deploy manuelle Dashboard-Arbeit nicht wischt
Der Anon-Key ist absichtlich öffentlich. Row Level Security ist das Schloss an der Tür. Ohne RLS ist Login Theater.
FAQ
Ist meine Supabase-Datenbank öffentlich, wenn ich RLS nie aktiviert habe?
Ja. Tabellen ohne Row Level Security erlauben jedem Request mit deinem Anon-Key das Lesen oder Schreiben von Zeilen — begrenzt nur durch Postgres-Grants. Der Anon-Key steckt in deinem Frontend-Bundle.
Warum funktionieren Inserts in der Preview, scheitern aber nach supabase rls?
RLS blockiert standardmäßig alle Zeilen, wenn aktiviert. Du brauchst eine INSERT-Policy, die authentifizierten Nutzern erlaubt, eigene Zeilen einzufügen. Ohne sie gibt Supabase permission denied zurück — obwohl derselbe Code vorher lief.
Ersetzt supabase rls die Authentifizierung?
Nein. Auth beweist, wer der Nutzer ist. RLS entscheidet, welche Zeilen dieser Nutzer sehen oder ändern darf. Du brauchst beides: Login und Policies, die Zeilen an auth.uid() binden.