FixVibe

// docs / baas security / supabase rls scanner

Supabase-RLS-Scanner: Tabellen mit fehlender oder defekter Row-Level Security finden

Row-Level Security (RLS) ist das Einzige, was zwischen den Daten deiner Kunden und dem Internet steht, wenn du eine Supabase-gestützte App ausrollst. KI-Coding-Tools generieren RLS-förmigen Code, der kompiliert, ausgeliefert wird und leise Daten leakt — Tabellen ohne aktiviertes RLS, Policies, die lesen aber nie einschränken, Prädikate, die eine Spalte mit sich selbst vergleichen. Dieser Artikel zeigt, was ein Supabase-RLS-Scanner von außen beweisen kann, die vier kaputten RLS-Formen, die in Vibe-coded Apps auftauchen, und wie du dein eigenes Deployment in unter einer Minute scannst.

Was ein externer RLS-Scan beweisen kann

Ein passiver RLS-Scan läuft gegen den PostgREST-Endpunkt, den Supabase unter https://[project].supabase.co/rest/v1/ bereitstellt. Er nutzt nur den veröffentlichbaren anon-Key — denselben, den dein Browser verwendet — und sondiert Tabellen-Listen-Metadaten, anonyme Lesezugriffe und anonyme Schreibzugriffe. Er authentifiziert sich nie als Nutzer und greift nie auf Service-Role-Privilegien zu. Alles, was er tun kann, kann ein nicht authentifizierter Angreifer im Internet auch tun.

Von außerhalb der Datenbank kann ein Scanner Folgendes mit hoher Konfidenz bestätigen:

  • RLS ist auf einer Tabelle deaktiviert. PostgREST liefert Zeilen für ein anonymes SELECT, wenn RLS aus ist oder eine Policy es erlaubt. Beides ist ein Befund.
  • Die anonyme Rolle kann Tabellen auflisten. Ein GET /rest/v1/ mit dem Anon-Key liefert das OpenAPI-Schema für jede Tabelle zurück, auf die die Rolle anon irgendein Privileg besitzt. KI-generierte Apps vergeben häufig USAGE auf das Schema und SELECT auf jede Tabelle, was die komplette Schemakarte preisgibt, selbst wenn RLS die eigentlichen Lesezugriffe verweigert.
  • Die anonyme Rolle kann INSERT ausführen. Ein sondierendes POST mit einem Ratet zur Spaltenform gelingt, wenn RLS keine INSERT-Policy hat, die es verweigert — selbst wenn SELECT gesperrt ist.
  • Der Service-Role-Key liegt im Browser-Bundle. Angrenzend an RLS: findet ein Scanner SUPABASE_SERVICE_ROLE_KEY oder irgendein JWT mit role: service_role im JavaScript-Bundle, ist RLS hinfällig — wer diesen Key besitzt, umgeht jede Policy.

Was ein externer Scan nicht beweisen kann

Sei ehrlich über die Grenzen des Scanners. Ein externer RLS-Scan kann deine pg_policies-Tabelle, deine Migrationsdateien und das exakte Prädikat einer Policy nicht lesen. Er schließt aus Black-Box-Verhalten, was bedeutet, dass er manchmal einen Befund meldet, der sich als beabsichtigte öffentliche Daten herausstellt (eine Marketing-Newsletter-Tabelle, ein öffentlicher Produktkatalog). Der FixVibe-Report markiert diese als mittlere Konfidenz, wenn der Scanner die Absicht nicht eindeutig bestimmen kann — schau dir den Tabellennamen an und entscheide.

Die vier kaputten RLS-Formen, die KI-Tools produzieren

Wenn du Cursor, Claude Code, Lovable oder Bolt auf Supabase ansetzt, tauchen über tausende Apps hinweg dieselben vier kaputten RLS-Muster auf. Jedes besteht die Typprüfung, kompiliert und wird ausgeliefert:

Form 1: RLS nie aktiviert

Der häufigste Fehlermodus. Die Migration erstellt die Tabelle, aber der Entwickler (oder das KI-Tool) vergisst ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST liefert die komplette Tabelle fröhlich an jeden mit dem Anon-Key aus. Fix: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY; ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;. FORCE ist nicht optional — ohne es umgehen Tabelleneigentümer (und jede Rolle mit Tabelleneigentümerschaft) RLS.

sql
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE  ROW LEVEL SECURITY;

Form 2: RLS aktiviert, keine Policies

Ein subtilerer Fehler. RLS ist aktiviert, aber es sind keine Policies geschrieben. Der Standard in PostgreSQL ist verweigern, also sehen authentifizierte Nutzer nichts — und der Entwickler fügt USING (true) hinzu, damit die App funktioniert, was allen erlaubt, alles zu lesen. Fix: schreibe eine Policy, die per auth.uid() einschränkt: CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); und eine passende INSERT/UPDATE/DELETE-Policy.

sql
CREATE POLICY "select_own"
  ON public.[name]
  FOR SELECT
  USING (auth.uid() = user_id);

Form 3: Policy vergleicht Spalte mit sich selbst

A copy-paste artefact. The developer writes <code>USING (user_id = user_id)</code> — which is always true — instead of <code>USING (auth.uid() = user_id)</code>. Type-checks pass; the policy permits every row. <strong>Fix:</strong> always compare a column to a function call (<code>auth.uid()</code>, <code>auth.jwt()->>'org_id'</code>, etc.), never to itself or to a constant.

Form 4: Policy auf SELECT, aber nicht auf INSERT/UPDATE

Der Entwickler sperrt Lesezugriffe, vergisst aber Schreibzugriffe. RLS-Policies sind pro Befehl. FOR SELECT schützt nur Lesezugriffe; ein anonymer Client kann immer noch INSERT, wenn keine Policy es verweigert. Fix: schreibe eine Policy pro Befehl oder verwende FOR ALL mit expliziten USING- und WITH CHECK-Klauseln.

Wie der Supabase-RLS-Scanner von FixVibe funktioniert

Der Check baas.supabase-rls läuft in drei Stufen, jede mit expliziten Konfidenzstufen:

  1. Stufe 1 — Fingerprinting. Der Scanner crawlt die deployte App, parst ihr JavaScript-Bundle und extrahiert die Supabase-Projekt-URL und den Anon-Key aus der Runtime-Konfiguration. Kein DNS-Raten, keine Brute-Force — er liest, was der Browser liest.
  2. Stufe 2 — Schema-Discovery. Ein einzelnes GET /rest/v1/ mit dem Anon-Key liefert das OpenAPI-Schema für jede Tabelle, die die Anon-Rolle sehen kann. Der Scanner notiert Tabellennamen, liest in dieser Stufe aber keine Zeilendaten.
  3. Stufe 3 — Lese- und Schreib-Sondierungen. Für jede entdeckte Tabelle gibt der Scanner ein anonymes SELECT mit limit=1 ab. Liefert das Zeilen zurück, ist RLS permissiv. Der Scanner stoppt dort — er zählt keine Zeilen auf, paginiert nicht, modifiziert keine Daten. INSERT-Sondierungen sind hinter verifizierter Domain-Eigentümerschaft und explizitem Opt-in eingegrenzt; sie laufen nie gegen unverifizierte Ziele.

Jeder Befund wird mit der exakten Request-URL, dem Response-Status, der Response-Form (nur Header) und dem Tabellennamen geliefert. Der KI-Fix-Prompt am Ende des Befunds ist ein Copy-Paste-SQL-Block, den du im Supabase-SQL-Editor ausführst.

Was tun, wenn der Scanner etwas findet

Jeder RLS-Befund ist ein Runtime-Notfall. Öffentliche PostgREST-Endpunkte werden binnen Minuten von Angreifern gescannt. Die Behebungssequenz ist mechanisch:

  1. Auditiere jede Tabelle. Führe SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; im Supabase-SQL-Editor aus. Jede Zeile mit rowsecurity = false ist ein Problem.
  2. Aktiviere RLS auf jeder öffentlichen Tabelle. Standardmäßig ENABLE ROW LEVEL SECURITY und FORCE ROW LEVEL SECURITY auf jeder erstellten Tabelle — mach es zur Migrations-Vorlage.
  3. Schreibe Policies Befehl für Befehl. Verwende nicht FOR ALL USING (true). Schreibe explizite Policies für SELECT, INSERT, UPDATE, DELETE — jede auf auth.uid() oder eine Org-ID-Spalte aus auth.jwt() eingegrenzt.
  4. Verifiziere mit einem zweiten Konto. Registriere dich als anderer Nutzer, versuche, die Datensätze eines anderen Nutzers direkt über die REST-API zu lesen. Ist die Antwort 200, ist die Policy kaputt.
  5. Erneut scannen. Nach Anwendung des Fix lass einen FixVibe-Scan erneut gegen dieselbe URL laufen. Der baas.supabase-rls-Befund sollte verschwinden.
sql
-- Audit every table for missing RLS. Run in the Supabase SQL editor.
SELECT schemaname, tablename, rowsecurity
FROM   pg_tables
WHERE  schemaname = 'public'
ORDER  BY rowsecurity, tablename;

Wie sich das mit anderen Scannern vergleicht

Die meisten generischen DAST-Tools (Burp Suite, OWASP ZAP, Nessus) wissen nicht, was PostgREST ist. Sie crawlen deine App, ignorieren den /rest/v1/-Pfad und berichten über die HTML-Seiten, die sie verstehen. Snyk und Semgrep sind statische Analysetools — sie finden Migrationsdateien in deinem Repo mit fehlenden RLS-Aufrufen, können aber nicht beweisen, dass die deployte Datenbank falsch konfiguriert ist. FixVibe sitzt in dieser Lücke: passiv, BaaS-bewusst, fokussiert darauf, was ein nicht authentifizierter Angreifer von der öffentlichen URL aus beweisen kann.

Häufig gestellte Fragen

Liest oder modifiziert der Scanner meine Daten?

Nein. Passive Scans senden höchstens ein SELECT ... limit=1 pro entdeckter Tabelle, um zu bestätigen, ob RLS anonyme Lesezugriffe erlaubt. Der Scanner notiert die Response-Form, nicht den Zeileninhalt. INSERT-, UPDATE- und DELETE-Sondierungen sind hinter verifizierter Domain-Eigentümerschaft eingegrenzt und laufen nie gegen unverifizierte Ziele.

Funktioniert das, wenn mein Supabase-Projekt pausiert ist oder auf einer eigenen Domain liegt?

Pausierte Projekte liefern 503 auf jede Anfrage — der Scanner meldet das Projekt als nicht erreichbar. Eigene Domains funktionieren, solange die deployte App das Supabase-Client-SDK im Browser lädt; der Scanner extrahiert die Projekt-URL ohnehin aus dem Bundle.

Was, wenn mein Anon-Key rotiert wird oder mein Publishable-Key sich ändert?

Erneut scannen. Der Scanner extrahiert den Key bei jedem Lauf neu aus dem aktuellen Bundle. Die Rotation invalidiert nur den vorherigen Report, nicht den Policy-Zustand der Datenbank.

Prüft der Scanner das neue Supabase-Publishable-Key-Modell (<code>sb_publishable_*</code>)?

Ja. Der Detektor erkennt sowohl die alten anon-JWTs als auch die neueren sb_publishable_*-Keys und behandelt sie identisch — beide sind als öffentlich gedacht und beide lassen RLS als einzige Verteidigungslinie zurück.

Nächste Schritte

Lass einen kostenlosen FixVibe-Scan gegen deine Produktions-URL laufen — der baas.supabase-rls-Check ist in jedem Tarif einschließlich der kostenlosen Stufe aktiviert. Für eine tiefere Lektüre, was sonst noch aus einem Supabase-Projekt leaken kann, siehe Supabase-Service-Role-Key in JavaScript offengelegt und Supabase-Storage-Bucket-Sicherheits-Checkliste. Für den Gesamtüberblick über alle BaaS-Anbieter siehe BaaS-Fehlkonfigurations-Scanner.

// scanne deine baas-oberfläche

Finde die offene Tabelle, bevor es jemand anderes tut.

Gib eine Produktions-URL ein. FixVibe ermittelt die BaaS-Anbieter, mit denen deine App spricht, identifiziert ihre öffentlichen Endpunkte und meldet, was ein nicht authentifizierter Client lesen oder schreiben kann. Kostenlos, ohne Installation, ohne Karte.

  • Kostenloser Tarif — 3 Scans pro Monat, ohne Anmeldekarte.
  • Passives BaaS-Fingerprinting — keine Domain-Verifizierung erforderlich.
  • Supabase, Firebase, Clerk, Auth0, Appwrite und mehr.
  • KI-Fix-Prompts bei jedem Befund — füge sie zurück in Cursor / Claude Code ein.
Kostenlosen BaaS-Scan starten

keine anmeldung erforderlich

Supabase-RLS-Scanner: Tabellen mit fehlender oder defekter Row-Level Security finden — Docs · FixVibe