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

A passive RLS scan looks at your Supabase project the way an anonymous visitor would. It uses only the publishable anon key — the same key your browser uses — and never authenticates as a user or touches service-role privileges. Anything it can see, an unauthenticated attacker on the internet can see.

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.
  • Read access does not establish write safety. A passive scan cannot prove that INSERT, UPDATE, or DELETE policies protect every user and tenant. Review write policies and test them with isolated fixtures under the intended roles.
  • 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

When you point Cursor, Claude Code, Lovable, or Bolt at Supabase, the same four broken-RLS patterns come up again and again. Each one passes type-check, compiles, and ships:

Form 1: RLS nie aktiviert

The most common failure mode. The migration creates the table but the developer (or the AI tool) forgets ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST happily serves the entire table to anyone with the anon key. Fix: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;, then add a policy per command. Enabling RLS with no policies denies all access through the API; the policies decide who gets in.

sql
ALTER TABLE public.[name] ENABLE 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

Ein Copy-Paste-Artefakt. Der Entwickler schreibt USING (user_id = user_id) — was immer wahr ist — statt USING (auth.uid() = user_id). Typprüfung besteht; die Policy erlaubt jede Zeile. Fix: vergleiche eine Spalte stets mit einem Funktionsaufruf (auth.uid(), auth.jwt()->>'org_id', etc.), nie mit sich selbst oder einer Konstanten.

Shape 4: Overly permissive write policies

With RLS enabled, a role with no applicable policy is denied by default. A SELECT policy does not itself allow INSERT. Write exposure can arise from a permissive applicable INSERT or ALL policy, disabled RLS, or a role that bypasses RLS. Fix: inspect table privileges and every applicable policy, use ownership-scoped WITH CHECK conditions, and test writes with separate user identities.

Wie der Supabase-RLS-Scanner von FixVibe funktioniert

FixVibe's Supabase check works from the deployed app, in three steps:

  1. Find the project. FixVibe loads the deployed app and reads the Supabase project URL and public key from the same configuration your browser receives. No guessing, no brute force.
  2. See what is exposed. FixVibe checks which tables the anonymous role can see, without reading row data at this step.
  3. Stage 3 — inspect anonymous read access. The passive check reports observable access to discovered resources. It does not create, update, or delete database rows, and it cannot establish that all write policies or cross-user boundaries are correct.

Each finding ships with the exact request URL, response status, response shape (header-only), and the table name. FixVibe now labels whether the fix belongs in code/config, a provider console, DNS, secret rotation, or manual review so the next step matches who can actually apply it.

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. Enable RLS on every public table. Default to ENABLE ROW LEVEL SECURITY on every table created — make it a migration template.
  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. Re-scan. After applying the fix, re-run a FixVibe scan against the same URL. The Supabase RLS finding should clear.
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?

The passive RLS check is read-only. It examines observable anonymous read access and reports limited evidence. It does not perform database write operations or prove that every user and tenant is correctly isolated.

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 (sb_publishable_*)?

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

Run a free FixVibe scan against your production URL — the Supabase RLS check runs on every plan including the free tier. For a deeper read on what else can leak from a Supabase project, see Supabase service role key exposed in JavaScript and Supabase storage bucket security checklist. For the umbrella view across all BaaS providers, read BaaS misconfiguration 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.
  • Coding-agent prompts where code/config applies, plus provider-console steps for hosted BaaS fixes.
Kostenlosen BaaS-Scan starten →

keine anmeldung erforderlich

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