Spring til indhold

SupabaseDatabasesikkerhedVibe coding

Supabase og Row Level Security: fejlen der lækker hele databasen

Den offentlige nøgle i jeres frontend er ikke et problem - den er designet. Problemet er de regler, der skulle have stået bag den.

lejenhacker.dk8 min. læsetid

Supabase er bygget på en model, mange udviklere misforstår: klienten taler direkte med databasen. Der er ingen backend imellem, der kan sige nej. Sikkerheden ligger udelukkende i Postgres’ egne regler for rækkeadgang - Row Level Security. Er de ikke sat op korrekt, er databasen reelt offentlig.

Hvorfor den offentlige nøgle er offentlig med vilje

Anon-nøglen i jeres frontend er ikke en hemmelighed, og den skal ikke skjules. Den identificerer projektet, ikke brugeren. Hele sikkerhedsmodellen bygger på, at Postgres selv afgør, hvilke rækker den, der spørger, må se. Beskyttelsen ligger i databasen - ikke i at ingen kender nøglen.

Det er en fornuftig model. Den fejler bare katastrofalt, hvis man tror, at nøglen var beskyttelsen.

Test det på 30 sekunder

Åbn jeres eget site i browseren, find anon-nøglen og projekt-URL’en i netværksfanen eller i det byggede bundle, og kør dette i konsollen på en tabel, I mener er beskyttet:

const res = await fetch(
  'https://DIT-PROJEKT.supabase.co/rest/v1/profiles?select=*',
  { headers: { apikey: ANON_KEY, Authorization: 'Bearer ' + ANON_KEY } }
)
console.log(await res.json())

Får I rækker tilbage - særligt rækker der tilhører andre brugere - er tabellen åben for alle på internettet. Får I en tom liste eller en fejl om manglende rettigheder, virker RLS på netop den tabel. Gentag for hver eneste tabel; det er meget almindeligt, at nogle er beskyttede og andre ikke.

De fire fejl vi finder oftest

RLS slået fra på en enkelt tabel. Ofte en tabel, der blev tilføjet senere end de andre - kommentarer, filer, invitationer, logs. Beskyttelsen blev sat op én gang tidligt og aldrig gentaget.

En politik, der reelt tillader alt. Formuleringer som "using (true)" optræder oftere, end man skulle tro, typisk indsat for at få noget til at virke under udvikling og aldrig strammet igen.

Manglende politik for skrivning. Læsning er beskyttet, men insert, update og delete er glemt. Enhver kan dermed ændre eller slette data, selvom de ikke kan læse dem - hvilket i praksis er værre.

Service role-nøglen brugt i klienten. Når RLS blokerer noget, er den hurtigste vej udenom den privilegerede nøgle, der ignorerer alle politikker. Ender den i frontenden, er samtlige regler irrelevante.

En opsætning der holder

-- 1. Slå RLS til på hver tabel med data der ikke må være offentlige
alter table profiles enable row level security;
alter table documents enable row level security;

-- 2. Eksplicitte politikker pr. handling
create policy "læs egen profil" on profiles
  for select using (auth.uid() = user_id);

create policy "opdater egen profil" on profiles
  for update using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

-- 3. Delt data via medlemskab frem for et flag i rækken
create policy "læs dokumenter i egen organisation" on documents
  for select using (
    org_id in (
      select org_id from memberships where user_id = auth.uid()
    )
  );

Fire ting der bør være på plads, før I lancerer

  • RLS er slået til på hver eneste tabel - ikke kun dem, I huskede
  • Der findes en politik pr. handling: select, insert, update og delete
  • Service role-nøglen findes udelukkende på serveren, aldrig i klientkoden
  • I har testet med to forskellige brugerkonti, at den ene ikke kan se den andens data

Det sidste punkt er det vigtigste og det, der oftest springes over. En test med én bruger beviser ingenting om isolation. Opret to konti, læg data på begge, og forsøg systematisk at nå den enes data med den andens session.

// RELATERET_YDELSE

Vibe Code Audit - sikkerhedsgennemgang af AI-genereret kode

En Vibe Code Audit er en sikkerhedsgennemgang af en applikation, der primært er bygget med AI-kodeværktøjer. Vi gennemgår både kildekoden og den kørende app for de fejlmønstre, sprogmodeller gentager systematisk - manglende adgangskontrol på API-endpoints, hemmeligheder i klientkoden, åbne databaseregler og autentifikation der kun håndhæves i frontenden.

Læs mere om vibe code audit →

Korte svar

Er det farligt at have Supabase anon-nøglen i frontenden?

Nej, anon-nøglen er designet til at være offentlig og skal ligge i frontenden. Faren opstår kun, hvis Row Level Security ikke er slået til, eller hvis politikkerne er for brede - for så er nøglen adgang til data frem for blot en identifikation af projektet.

Hvordan tester jeg om min Supabase-database er sikker?

Åbn browserkonsollen på jeres eget site og kald REST-API’et direkte med anon-nøglen mod hver tabel. Får I rækker tilbage, som den aktuelle bruger ikke burde kunne se, er tabellen ubeskyttet. Gentag testen med to forskellige brugerkonti for at verificere, at data er isoleret mellem brugere.

Skal vi kigge jeres systemer efter i sømmene?

Book et gratis afklaringsmøde på 30 minutter. Ingen salgstale - bare en ærlig vurdering af, hvor I står, og hvad der giver mening at gøre først.

Svar under 4 timer på hverdage · NDA underskrives inden første tekniske møde