Supabase Row Level Security (RLS): sichere deine Tabellen ab
Supabase stellt deine Postgres-Tabellen über seine Data API direkt dem Browser zur Verfügung, authentifiziert mit dem öffentlichen anon key (Publishable Key). Policies für Row Level Security (RLS) entscheiden, welche Zeilen jeder Nutzer lesen oder ändern darf. Eine Tabelle im public-Schema ohne RLS ist für jeden lesbar — und oft auch beschreibbar —, der deine Website öffnet.
Was es ist
Jedes Supabase-Projekt bringt einen anon key (oder sb_publishable_-Key) mit, der für dein Frontend gedacht ist. Er identifiziert dein Projekt, nicht deinen Nutzer — jeder kann ihn aus deinem JavaScript-Bundle kopieren und selbst Requests an https://<project>.supabase.co/rest/v1/ schicken, komplett an deiner UI vorbei.
Row Level Security ist ein Postgres-Feature, das Policies an eine Tabelle hängt: SQL-Bedingungen, die bei jedem SELECT, INSERT, UPDATE und DELETE für jede einzelne Zeile geprüft werden. Ist RLS aktiviert und es gibt keine Policies, ist über die API nichts zugänglich. Jede Policy, die du hinzufügst, öffnet genau eine Tür — zum Beispiel „angemeldete Nutzer dürfen Zeilen lesen, deren user_id ihrer eigenen ID entspricht“.
Warum es wichtig ist
Eine fehlende oder zu weit gefasste Policy ist kein kleiner Bug, sondern ein vollständiges Datenleck: E-Mail-Adressen, Profile, Bestellungen, private Nachrichten — was immer in der Tabelle steht — lassen sich mit einem einzigen curl-Befehl abziehen. KI-Coding-Tools legen Tabellen häufig über SQL-Migrationen an, bei denen RLS standardmäßig aus ist, und generieren freizügige Platzhalter-Policies wie using (true), nur damit die App funktioniert.
Der service_role-Key (sb_secret_) umgeht RLS vollständig. Landet er in clientseitigem Code, ist jede Policy, die du geschrieben hast, wirkungslos — wer ihn besitzt, hat vollen Lese- und Schreibzugriff auf deine Datenbank.
So behebst du es
Führe das im Supabase SQL Editor aus, um jede Tabelle im exponierten public-Schema aufzulisten, bei der RLS noch ausgeschaltet ist. Der Security Advisor im Dashboard meldet dasselbe Problem als „RLS disabled in public“.
select tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false;Schalte RLS für jede Tabelle ein und füge dann pro Operation eine Policy hinzu. Beschränke Policies auf die Rolle authenticated und vergleiche die Owner-Spalte der Zeile mit auth.uid(). Inserts und Updates brauchen with check, damit Nutzer keine Zeilen schreiben können, die jemand anderem gehören.
alter table public.profiles enable row level security;
create policy "Users read own profile"
on public.profiles for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Users update own profile"
on public.profiles for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);Ersetze Platzhalter-Policies wie using (true) durch echte Bedingungen. Stütze die Autorisierung nicht auf user_metadata — das können Nutzer selbst bearbeiten; verwende stattdessen app_metadata oder eine Rollentabelle. Views umgehen RLS, sofern sie nicht mit security_invoker = true erstellt werden, und Storage-Buckets brauchen eigene Policies auf storage.objects.
Frage deine API genau so ab, wie es ein Angreifer tun würde: nur mit dem öffentlichen Key und ohne Nutzer-Session. Liefert das Zeilen zurück, die nicht öffentlich sein sollen, sind deine Policies zu offen.
curl "https://<project>.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: <anon-key>" \
-H "Authorization: Bearer <anon-key>"Verwende den service_role-Key (sb_secret_) nur in Server-Code wie API-Routen oder Edge Functions, niemals hinter einem NEXT_PUBLIC_- oder VITE_-Präfix. Wurde er jemals an den Browser ausgeliefert, rotiere ihn im Dashboard.
FAQ
Ist es sicher, dass mein Supabase anon key im Browser sichtbar ist?
Ja — der anon key (Publishable Key) ist dafür gedacht, öffentlich zu sein, aber nur, wenn jede exponierte Tabelle RLS mit korrekten Policies aktiviert hat. Der Key selbst erlaubt nichts, was deine Policies nicht zulassen.
Ich habe RLS aktiviert und jetzt liefert meine App leere Ergebnisse. Ist sie kaputt?
Nein, genau so soll RLS arbeiten: Ohne Policies wird jede Zeile verweigert. Füge Policies für die Operationen hinzu, die deine App braucht, beschränkt auf die richtigen Nutzer, statt RLS wieder auszuschalten.
Testet AppSafe meine RLS-Policies?
AppSafe meldet sich nicht bei deiner Datenbank an und kann deine Policies daher nicht auswerten — nutze die SQL- und curl-Checks oben. Es durchsucht aber dein JavaScript nach Supabase-Keys. Wird einer gemeldet, dekodiere den mittleren Teil des JWT und prüfe den role-Claim: anon ist im Browser zu erwarten, service_role ist ein kritisches Leck.
Ist deine App betroffen?
AppSafe prüft dies und Dutzende weitere Probleme in einem kostenlosen Scan.
Meine App gratis scannenVerwandte Guides
Firebase Security Rules: lass deine Datenbank nicht offen
Deine Firebase-Config ist absichtlich öffentlich. Deine Rules entscheiden, wer die Daten sieht.
API-Keys, die in clientseitigem JavaScript offengelegt sind
Alles in deinem Bundle ist öffentlich. Behandle es entsprechend.
Offene Datenbank-Backups: was passiert, wenn /backup.sql erreichbar ist
Ein erreichbarer .sql-Dump ist die komplette Übergabe deiner gesamten Datenbank.