FixVibe

// docs / baas security / supabase rls scanner

Supabase RLS-skanner: finn tabeller med manglende eller ødelagt row-level security

Row-level security (RLS) er det eneste som står mellom kundenes data og internett når du lanserer en Supabase-basert app. AI-kodeverktøy genererer RLS-formet kode som kompilerer, leveres og lekker data i stillhet — tabeller opprettet uten RLS aktivert, policyer som leser men aldri begrenser, predikater som sammenligner en kolonne med seg selv. Denne artikkelen viser hva en Supabase RLS-skanner kan bevise utenfra, de fire formene av ødelagt RLS som dukker opp i vibe-kodede apper, og hvordan du skanner din egen deployment på under et minutt.

Hva en ekstern RLS-skanning kan bevise

En passiv RLS-skanning kjører mot PostgREST-endepunktet som Supabase eksponerer på https://[project].supabase.co/rest/v1/. Den bruker bare den publiserbare anon-nøkkelen — samme nøkkel som nettleseren din bruker — og sonderer etter tabellisten-metadata, anonyme lesinger og anonyme skrivinger. Den autentiserer seg aldri som en bruker og rører aldri service-role-privilegier. Alt den kan gjøre, kan en uautentisert angriper på internett også gjøre.

Utenfra databasen kan en skanner bekrefte følgende med høy konfidens:

  • RLS er deaktivert på en tabell. PostgREST returnerer rader for en anonym SELECT når RLS er av eller når en policy tillater det. Begge tilfellene er et funn.
  • Den anonyme rollen kan liste tabeller. En GET /rest/v1/ med anon-nøkkelen returnerer OpenAPI-skjemaet for hver tabell som rollen anon har noe privilegium på. AI-genererte apper gir ofte USAGE på skjemaet og SELECT på hver tabell, noe som eksponerer hele skjemakartet selv om RLS nekter selve lesingene.
  • Den anonyme rollen kan utføre INSERT. En sonderende POST med en gjetning på kolonneformen lykkes hvis RLS ikke har en INSERT-policy som nekter det — selv om SELECT er låst.
  • Service-role-nøkkelen ligger i nettleserbundlen. Tilstøtende RLS: hvis en skanner finner SUPABASE_SERVICE_ROLE_KEY eller en hvilken som helst JWT med role: service_role i JavaScript-bundlen, er RLS irrelevant — den som har nøkkelen omgår enhver policy.

Hva en ekstern skanning ikke kan bevise

Vær ærlig om skannerens grenser. En ekstern RLS-skanning kan ikke lese pg_policies-tabellen din, migrasjonsfilene dine eller det eksakte predikatet for noen policy. Den slutter seg fra black-box-atferd, noe som betyr at den noen ganger vil rapportere et funn som viser seg å være tilsiktet offentlig data (en markedsføringsnyhetsbrev-tabell, en offentlig produktkatalog). FixVibe-rapporten flagger disse som middels konfidens når skanneren ikke kan skille intensjonen — gjennomgå tabellnavnet og avgjør.

De fire formene av ødelagt RLS som AI-verktøy produserer

Når du retter Cursor, Claude Code, Lovable eller Bolt mot Supabase, dukker de samme fire mønstrene for ødelagt RLS opp på tvers av tusenvis av apper. Hvert av dem består typekontroll, kompilerer og leveres:

Form 1: RLS aldri aktivert

Den vanligste feilmodusen. Migrasjonen oppretter tabellen, men utvikleren (eller AI-verktøyet) glemmer ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST serverer glade i livet hele tabellen til enhver med anon-nøkkelen. Fiks: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY; ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;. FORCE er ikke valgfritt — uten det omgår tabelleieren (og enhver rolle med tabelleierskap) RLS.

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

Form 2: RLS aktivert, ingen policyer

En mer subtil feil. RLS er aktivert, men ingen policyer er skrevet. Standarden i PostgreSQL er nekt, så autentiserte brukere ser ingenting — og utvikleren legger til USING (true) for å få appen til å fungere, noe som lar alle lese alt. Fiks: skriv en policy som avgrenses via auth.uid(): CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); og en matchende INSERT/UPDATE/DELETE-policy.

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

Form 3: Policy sammenligner kolonne med seg selv

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 på SELECT, men ikke på INSERT/UPDATE

Utvikleren låser lesinger, men glemmer skrivinger. RLS-policyer er per kommando. FOR SELECT beskytter bare lesinger; en anonym klient kan fortsatt INSERT hvis ingen policy nekter det. Fiks: skriv en policy per kommando, eller bruk FOR ALL med eksplisitte USING- og WITH CHECK-klausuler.

Slik fungerer FixVibes Supabase RLS-skanner

baas.supabase-rls-sjekken kjører i tre trinn, hvert med eksplisitte konfidensnivåer:

  1. Trinn 1 — fingeravtrykking. Skanneren crawler den deployede appen, parser dens JavaScript-bundle og henter ut Supabase-prosjektets URL og anon-nøkkel fra runtime-konfigurasjonen. Ingen DNS-gjetting, ingen brute force — den leser det nettleseren leser.
  2. Trinn 2 — skjemaoppdagelse. En enkelt GET /rest/v1/ med anon-nøkkelen returnerer OpenAPI-skjemaet for hver tabell anon-rollen kan se. Skanneren noterer tabellnavn, men leser ingen raddata i dette trinnet.
  3. Trinn 3 — lese- og skrivesonderinger. For hver oppdaget tabell utsteder skanneren en anonym SELECT med limit=1. Hvis rader returneres, er RLS tillatende. Skanneren stopper der — den teller ikke rader, paginerer ikke, modifiserer ikke data. INSERT-sonderinger er begrenset bak verifisert domeneierskap og eksplisitt opt-in; de kjøres aldri mot uverifiserte mål.

Hvert funn leveres med den eksakte request-URL-en, response-statusen, response-formen (kun header) og tabellnavnet. AI-fiksprompten nederst i funnet er en copy-paste-SQL-blokk som du kjører i Supabase SQL-editoren.

Hva du gjør når skanneren finner noe

Hvert RLS-funn er en runtime-nødssituasjon. Offentlige PostgREST-endepunkter blir skannet av angripere innen minutter. Utbedringssekvensen er mekanisk:

  1. Granske hver tabell. Kjør SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; i Supabase SQL-editoren. Hver rad med rowsecurity = false er et problem.
  2. Aktivere RLS på hver offentlige tabell. Standard til ENABLE ROW LEVEL SECURITY og FORCE ROW LEVEL SECURITY på hver tabell som opprettes — gjør det til en migrasjonsmal.
  3. Skrive policyer kommando-for-kommando. Ikke bruk FOR ALL USING (true). Skriv eksplisitte policyer for SELECT, INSERT, UPDATE, DELETE — hver av dem avgrenset til auth.uid() eller en org-id-kolonne fra auth.jwt().
  4. Verifisere med en annen konto. Registrer deg som en annen bruker, forsøk å lese en annen brukers data direkte via REST-API-et. Hvis svaret er 200, er policyen ødelagt.
  5. Skanne på nytt. Etter at fiksen er anvendt, kjør en FixVibe-skanning mot samme URL på nytt. baas.supabase-rls-funnet skal forsvinne.
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;

Hvordan dette sammenlignes med andre skannere

De fleste generiske DAST-verktøy (Burp Suite, OWASP ZAP, Nessus) vet ikke hva PostgREST er. De crawler appen din, ignorerer /rest/v1/-stien og rapporterer på HTML-sidene de forstår. Snyk og Semgrep er statisk-analyse-verktøy — de finner migrasjonsfiler i repoet ditt med manglende RLS-kall, men de kan ikke bevise at den deployede databasen er feilkonfigurert. FixVibe sitter i gapet: passiv, BaaS-bevisst, fokusert på det en uautentisert angriper kan bevise fra den offentlige URL-en.

Ofte stilte spørsmål

Vil skanneren lese eller modifisere dataene mine?

Nei. Passive skanninger utsteder maksimalt én SELECT ... limit=1 per oppdaget tabell for å bekrefte om RLS tillater anonyme lesinger. Skanneren noterer response-formen, ikke radinnholdet. INSERT-, UPDATE- og DELETE-sonderinger er begrenset bak verifisert domeneierskap og kjøres aldri mot uverifiserte mål.

Fungerer dette hvis Supabase-prosjektet mitt er pauset eller på et tilpasset domene?

Pausede prosjekter returnerer 503 på hver request — skanneren rapporterer prosjektet som utilgjengelig. Tilpassede domener fungerer så lenge den deployede appen fortsatt laster Supabase-klient-SDK-en i nettleseren; skanneren henter prosjekt-URL-en fra bundlen uansett.

Hva hvis anon-nøkkelen min roteres eller den publiserbare nøkkelen min endres?

Kjør skanningen på nytt. Skanneren henter nøkkelen på nytt fra den nåværende bundlen ved hver kjøring. Rotasjon invaliderer kun den forrige rapporten, ikke databasens policy-tilstand.

Sjekker skanneren den nye Supabase publiserbare-nøkkel-modellen (<code>sb_publishable_*</code>)?

Ja. Detektoren gjenkjenner både eldre anon-JWT-er og de nyere sb_publishable_*-nøklene og behandler dem identisk — begge er ment å være offentlige, og begge lar RLS være den eneste forsvarslinjen.

Neste steg

Kjør en gratis FixVibe-skanning mot produksjons-URL-en din — baas.supabase-rls-sjekken er aktivert på alle planer, inkludert gratisnivået. For en dypere lesning av hva annet som kan lekke fra et Supabase-prosjekt, se Supabase service-role-nøkkel eksponert i JavaScript og Sjekkliste for Supabase storage-bucket-sikkerhet. For helhetsbildet på tvers av alle BaaS-leverandører, les BaaS-feilkonfigurasjonsskanner.

// skann din baas-flate

Finn den åpne tabellen før noen andre gjør det.

Lim inn en produksjons-URL. FixVibe identifiserer BaaS-leverandørene appen din snakker med, fingeravtrykker deres offentlige endepunkter, og rapporterer hva en uautentisert klient kan lese eller skrive. Gratis, ingen installasjon, intet kort.

  • Gratisnivå — 3 skanninger/måned, intet kort ved registrering.
  • Passiv BaaS-fingeravtrykking — ingen domeneverifisering nødvendig.
  • Supabase, Firebase, Clerk, Auth0, Appwrite og flere.
  • AI-fiksforslag på hvert funn — lim tilbake i Cursor / Claude Code.
Kjør en gratis BaaS-skanning

ingen registrering nødvendig

Supabase RLS-skanner: finn tabeller med manglende eller ødelagt row-level security — Docs · FixVibe