Alle Guides
Schweregrad KritischDatenbanksicherheit

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

Finde Tabellen ohne RLS

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;
Aktiviere RLS und schreibe Owner-only-Policies

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);
Vermeide die typischen Policy-Fehler

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.

Teste von außen mit dem anon key

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>"
Halte den service_role-Key auf dem Server

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 scannen