Supabase Row Level Security einschalten, ohne die App kaputt zu machen
Die häufigste Ursache dafür, dass Fremde die Daten einer KI-gebauten App lesen können, ist eine einzige fehlende Einstellung. Hier steht, warum sie fehlt, wie du sie setzt, und wie du danach von außen nachweist, dass die Tabelle wirklich zu ist.
Wenn du dir den Quelltext deiner App ansiehst, findest du dort die Adresse deines Supabase-Projekts und einen langen Schlüssel, den anon key. Das sieht nach einem Leck aus, ist aber keins: Dieser Schlüssel gehört genau dorthin. Er sagt der Datenbank nur, welches Projekt gemeint ist, und er darf öffentlich sein.
Was den Zugriff tatsächlich begrenzt, ist Row Level Security, kurz RLS. Das ist eine Regel pro Tabelle, die festlegt, welche Zeilen eine anfragende Person sehen darf. Ist RLS aus, gilt für die Tabelle keine Einschränkung, und der öffentliche Schlüssel reicht, um alles zu lesen.
Und genau hier liegt die Falle: Bei einer neu angelegten Tabelle ist RLS aus, bis jemand sie einschaltet. Die KI legt Tabellen an, baut die App darum, alles funktioniert, und niemand schaltet etwas ein. Es sieht nicht kaputt aus. Es fällt erst auf, wenn jemand danach sucht.
RLS einschalten und eine Regel schreiben sind zwei verschiedene Schritte, und viele Anleitungen verschweigen den zweiten. Wenn du RLS einschaltest, ohne eine Regel zu hinterlegen, ist die Tabelle für alle zu, auch für deine eigene App. Das ist sicher, aber deine Seite zeigt dann leere Listen.
Die richtige Reihenfolge ist also: RLS einschalten, dann pro Tabelle festlegen, wer was darf. Für die meisten Tabellen in einer kleinen App gibt es genau zwei Muster, und mit denen kommst du sehr weit.
Finde heraus, welche Tabellen überhaupt offen sind
In Supabase unter Table Editor siehst du pro Tabelle einen Hinweis, ob RLS aktiv ist. Schneller geht es mit einer Abfrage im SQL Editor, die dir alle Tabellen im öffentlichen Schema ohne RLS auflistet.
select tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;Schalte RLS für jede dieser Tabellen ein
Eine Zeile pro Tabelle. Ab diesem Moment antwortet die Tabelle niemandem mehr, bis du im nächsten Schritt eine Regel ergänzt. Mach das also in einem Zug.
alter table public.profiles enable row level security;
alter table public.bookings enable row level security;Muster A: jede Person sieht nur ihre eigenen Zeilen
Das ist der Normalfall für alles, was Nutzern gehört: Profile, Bestellungen, Nachrichten, Aufgaben. Voraussetzung ist eine Spalte, in der die Nutzerkennung steht, meistens user_id. Das select in der Klammer ist kein Schönheitsfehler, sondern sorgt dafür, dass die Kennung einmal pro Abfrage ermittelt wird statt einmal pro Zeile.
create policy "eigene Zeilen lesen" on public.bookings
for select using ((select auth.uid()) = user_id);
create policy "eigene Zeilen anlegen" on public.bookings
for insert with check ((select auth.uid()) = user_id);
create policy "eigene Zeilen aendern" on public.bookings
for update using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);
create policy "eigene Zeilen loeschen" on public.bookings
for delete using ((select auth.uid()) = user_id);Muster B: alle dürfen lesen, niemand darf schreiben
Für Inhalte, die ohnehin öffentlich sind: Blogbeiträge, Produktliste, Speisekarte. Wichtig ist, dass du nur das Lesen freigibst. Ohne eine insert- oder update-Regel kann trotzdem niemand etwas hineinschreiben.
create policy "oeffentlich lesbar" on public.posts
for select using (true);Wenn du nicht selbst im Code arbeitest
Du musst das nicht von Hand tippen. Der folgende Text funktioniert als Anweisung in Lovable, Bolt, v0 oder Cursor. Lass danach trotzdem laufen, was in Schritt 1 steht, und prüf die Liste: Builder übersehen gerne einzelne Tabellen.
Aktiviere Row Level Security für jede Tabelle im public-Schema.
Ergaenze fuer jede Tabelle mit einer user_id-Spalte Policies fuer
select, insert, update und delete, die nur eigene Zeilen freigeben
((select auth.uid()) = user_id). Fuer rein oeffentliche Inhalts-
tabellen nur eine select-Policy mit using (true), keine Schreib-
Policy. Aendere nichts an den bestehenden Daten.Such den service_role-Schlüssel im ausgelieferten Code
Es gibt einen zweiten Schlüssel, den service_role key. Der umgeht jede Regel, die du gerade geschrieben hast. Er gehört ausschließlich auf den Server und nie in den Browser. Such im Quelltext deiner Seite nach service_role und nach Variablennamen, die mit einem öffentlichen Präfix beginnen und SECRET oder SERVICE enthalten, etwa VITE_SUPABASE_SERVICE_ROLE_KEY. Findest du so etwas, tausch den Schlüssel sofort im Supabase-Dashboard aus und nimm ihn aus dem Build.
Prüf von außen, ob es gewirkt hat
Der Test, der zählt, kommt nicht aus deiner eigenen App, denn die ist ja angemeldet. Er kommt von außen, mit dem öffentlichen Schlüssel, ohne Login. Das kannst du selbst im Terminal machen, oder du lässt Deckproof die Adresse prüfen: wir fragen genau eine Zeile pro Tabelle ab und sagen dir, was zurückkam.
curl -s "https://DEIN-PROJEKT.supabase.co/rest/v1/bookings?select=*&limit=1" \
-H "apikey: DEIN_ANON_KEY"Kommt eine leere Liste zurück, greift die Regel. Kommen Zeilen zurück, ist entweder RLS für diese Tabelle noch aus, oder eine Regel gibt mehr frei als gedacht, zum Beispiel eine select-Policy mit using (true) auf einer Tabelle mit Nutzerdaten.
Eine Sache bleibt dabei unauflösbar, und das sollte jede ehrliche Anleitung sagen: Eine leere Tabelle sieht von außen genauso aus wie eine geschützte. Wenn du sicher sein willst, leg testweise eine Zeile an und prüf dann noch einmal.
ADRESSE DEINER WEBSITE EINFÜGENNur, wenn du keine Regeln ergänzt. Mit den Mustern oben funktioniert die App für angemeldete Nutzer weiter. Wenn plötzlich Listen leer sind, fehlt fast immer eine select-Policy oder die Spalte heißt anders als user_id.
Dann musst du entscheiden, woran der Zugriff hängt. Häufig führt ein Weg über eine verknüpfte Tabelle, etwa eine Team-Zugehörigkeit. Bis du das geklärt hast, ist RLS einschalten ohne Regel die sichere Zwischenlösung: niemand kommt ran, auch kein Fremder.
Nein. Das versteckt sie nur in der automatischen Dokumentation. Wer den Namen kennt oder errät, und der steht meist im JavaScript deiner eigenen Seite, kommt weiterhin ran.
Nach jeder Änderung, bei der die KI neue Tabellen angelegt hat. Genau das ist der Moment, in dem wieder eine Tabelle ohne Regel entsteht, ohne dass es jemand merkt.
Supabase-Check: kann ein Fremder deine Tabellen lesen? · Die Studie: bei 11 von 38 Datenbanken stand etwas offen
Technische Anleitung. Rechtliche Hinweise darin sind allgemeine Informationen mit Verweis auf die jeweilige Regel, keine Rechtsberatung und keine Bewertung deines Einzelfalls.