Hoppa till innehållet
Blogg

8 min läsning

Lovable och Supabase – kontrollera RLS före lansering

Inloggning skyddar inte automatiskt din data. Så granskar du tabeller, filer och åtkomstregler i en Lovable-app med Supabase.

Av Skriven med AI-stöd utifrån källan

Dimma över en mörk klippig kust med lugnt hav
Foto: Leslie Cross på Unsplash

Vad granskningen faktiskt visar

En omfattande säkerhetsgranskning av Lovable-projekt har hittat appar där personuppgifter och andra känsliga uppgifter gick att läsa utan inloggning. Bland innehållet fanns chattar, identitetsuppgifter, interna meddelanden, avtal och dokument. Nyheten har uppmärksammats av Breakit, medan den tekniska metoden och avgränsningarna beskrivs i den ursprungliga granskningen.

Det betyder inte att alla Lovable-appar är osäkra eller att Lovables egna system har blivit hackade. Granskningen handlar främst om appar som kopplats till Supabase och vars åtkomstregler tillät obehöriga förfrågningar. Forskarna använde e-postadresser som en signal om att tabeller kunde innehålla personuppgifter, men påpekar att vissa träffar kan ha varit offentliga kontaktuppgifter eller testdata. De kunde inte heller fastställa om uppgifterna faktiskt hade missbrukats.

Det nya är därför inte en enskild sårbarhet som kan lösas genom att uppdatera ett paket. Granskningen visar i stället hur ett återkommande konfigurationsfel kan få stora konsekvenser när appar byggs och publiceras snabbt. Gränssnittet kan se färdigt ut, inloggningen kan fungera och användaren kan skickas till rätt sida – samtidigt som databasen accepterar anrop som gränssnittet aldrig visar.

Det viktigaste för dig som bygger är att skilja mellan vad en användare ser i appen och vad användaren kan hämta från databasen.

Inloggning är inte samma sak som behörighet

Autentisering svarar på frågan: Vem är användaren? Supabase Auth kan exempelvis verifiera att en person har loggat in och ge webbläsaren en signerad identitet.

Behörighet svarar på en annan fråga: Vad får den här användaren göra? En inloggad kund ska kanske få läsa sin egen profil, men inte andra kunders profiler. En medlem i ett projekt ska kanske få läsa projektets meddelanden, men inte meddelanden från andra projekt.

Det är här Row Level Security, förkortat RLS, kommer in. RLS är regler i Postgres-databasen som avgör vilka databasrader en förfrågan får läsa, skapa, ändra eller radera. Regeln körs i databasen och gäller även om någon går runt gränssnittet och anropar Supabase direkt. Supabase rekommenderar att RLS aktiveras på alla tabeller som exponeras genom dess Data API.

Ett vanligt misstag är en policy som i praktiken säger att alla inloggade användare får läsa alla rader. Rollen authenticated betyder bara att personen har en giltig session. Den betyder inte att personen äger raden, tillhör rätt organisation eller deltar i rätt konversation.

En annan missuppfattning är att Supabase-nyckeln i frontend måste döljas. Den publika nyckeln är avsedd att finnas i webbläsaren. Säkerheten ska inte bero på att ingen hittar den, eftersom den går att läsa i appens nätverkstrafik och byggda JavaScript. Det är RLS och databasens övriga behörigheter som ska begränsa vad nyckeln kan användas till. En hemlig nyckel eller äldre service_role-nyckel är däremot känslig och får inte skickas till webbläsaren, eftersom den kringgår RLS.

Granska tabellerna och deras policies

Börja med att inventera vilka tabeller appen använder. Titta inte bara på uppenbara tabeller som profiles och messages. Kontrollera även tabeller för formulärsvar, supportärenden, bokningar, betalningsmetadata, AI-genererat innehåll, väntelistor och administrativa anteckningar.

I Supabase Dashboard kan du öppna tabellerna och se om RLS är aktiverat. Du kan också använda SQL Editor för att få en samlad lista:

sql
select
  n.nspname as schema_name,
  c.relname as table_name,
  c.relrowsecurity as rls_enabled,
  c.relforcerowsecurity as rls_forced
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where c.relkind = 'r'
  and n.nspname = 'public'
order by c.relname;

Att rls_enabled är aktiverat är nödvändigt, men inte tillräckligt. Du måste också läsa policyerna och förstå vad de tillåter. Leta särskilt efter villkor som alltid blir sanna, bred åtkomst för anon eller authenticated och regler som kontrollerar inloggning men inte ägarskap.

För en enkel tabell där varje rad ägs av en användare kan principen vara att radens user_id måste motsvara den inloggade användarens identitet. Chattar, team och organisationer kräver ofta en medlemskapstabell i stället. En läspolicy för meddelanden kan exempelvis bygga på att användaren finns i den aktuella konversationens medlemslista:

sql
alter table public.messages enable row level security;
​
create policy "members can read conversation messages"
on public.messages
for select
to authenticated
using (
  exists (
    select true
    from public.conversation_members cm
    where cm.conversation_id = messages.conversation_id
      and cm.user_id = auth.uid()
  )
);

Kopiera inte exemplet rakt in utan att anpassa det till din datamodell. Policyn reglerar bara läsning. Skapande, ändring och radering behöver egna beslut. Fråga för varje operation vem som ska få göra den och under vilka villkor.

Kontrollera även databasens grants. Grants avgör om en roll över huvud taget får nå en tabell eller operation, medan RLS avgör vilka rader rollen får arbeta med. En tabell som aldrig behöver läsas direkt från frontend bör inte få bredare rättigheter än nödvändigt.

Glöm inte Storage, funktioner och administrativa flöden

Privata dokument ligger ofta i Supabase Storage i stället för i en vanlig tabell. Där räcker det inte att tabellen med dokumentmetadata är skyddad. Själva filen måste också vara privat.

Kontrollera varje bucket i Storage. En publik bucket gör filer tillgängliga via publika adresser och passar därför bara innehåll som verkligen ska kunna visas offentligt, exempelvis öppna profilbilder eller bilder på en publik webbplats. Passkopior, avtal, bilagor, kvitton och privata foton ska ligga i en privat bucket. Åtkomsten till privata filer styrs genom policies på storage.objects eller genom signerade länkar som skapas efter en behörighetskontroll.

Testa mer än uppladdningen. Kontrollera vem som kan:

  • lista filer i en bucket
  • ladda ned en fil med känt filnamn
  • ladda upp filer till någon annans mapp
  • ersätta eller radera en befintlig fil
  • skapa en signerad länk

Granska också Edge Functions, serverrutter och RPC-funktioner. Kod som använder en hemlig Supabase-nyckel kan kringgå RLS. Det kan vara avsiktligt för administrativa jobb, men då måste funktionen själv kontrollera användarens identitet och behörighet innan den läser eller ändrar data.

Ett vanligt riskmönster är att en funktion tar emot ett user_id, project_id eller filnamn från webbläsaren och litar på värdet. Användaren kan ändra sådana värden. Funktionen bör i stället härleda identiteten från den verifierade sessionen och kontrollera medlemskap eller roll på serversidan.

Testa utan att lita på gränssnittet

En säkerhetskontroll blir mer tillförlitlig om du testar appen som olika slags användare. Det räcker inte att klicka igenom det tänkta flödet med ditt administratörskonto.

Skapa separata testkonton, exempelvis användare A och användare B. Lägg dem i olika organisationer eller projekt och skapa tydligt märkta testposter. Försök sedan att nå användare A:s data från användare B:s session.

Använd webbläsarens Network-panel för att se vilka anrop appen gör till Supabase. Testa därefter samma typer av anrop när du är utloggad och när du är inloggad med fel konto. Kontrollera läsning, skapande, ändring och radering var för sig. Att läsning är blockerad betyder inte automatiskt att ändring eller radering också är blockerad.

Gå igenom följande före lansering och efter varje förändring av databasen:

  • Alla exponerade tabeller har RLS aktiverat
  • Policyerna kontrollerar ägarskap, medlemskap eller uttrycklig roll
  • Utloggade anrop får bara offentlig data
  • Ett konto kan inte läsa eller ändra ett annat kontos privata data
  • Administratörsåtkomst verifieras i databasen eller på serversidan
  • Privata filer ligger i privata buckets
  • Edge Functions och serverrutter gör egna behörighetskontroller
  • Ingen hemlig nyckel finns i frontend, loggar eller publikt källkodslager
  • Gamla testtabeller, buckets och funktioner har tagits bort eller låsts
  • Testet upprepas efter schemaändringar och nya Lovable-prompter

Lovables och Supabases automatiska kontroller kan hitta vanliga problem, men de kan inte fullt ut avgöra din affärslogik. Ett verktyg vet inte automatiskt om en supportmedarbetare ska få se alla ärenden, bara tilldelade ärenden eller inga bilagor alls. Den behörighetsmodellen måste du formulera och sedan bevisa med tester.

Om data redan kan ha varit offentlig

Om du upptäcker en öppen tabell eller bucket bör du inte nöja dig med att lägga till en policy och gå vidare. Stäng åtkomsten, spara relevanta loggar och dokumentera vilken data som kunde nås, under vilken period och av vilka roller eller nycklar.

Om en hemlig nyckel har funnits i frontend eller ett publikt källkodslager behöver den bytas. Granska även Edge Functions, miljövariabler, byggloggar och tidigare distributioner. En borttagen rad i den senaste koden gör inte automatiskt äldre nycklar ogiltiga.

Undersök därefter om det finns tecken på obehöriga anrop och vilka personer som kan vara berörda. Känsliga personuppgifter, identitetshandlingar och privata meddelanden kräver en mer försiktig incidentbedömning än offentligt innehåll eller rena testposter. Ta vid behov hjälp med teknisk utredning och bedömning av eventuella informations- eller anmälningskrav.

Målet är inte bara att få säkerhetsvarningarna att försvinna. Du ska kunna beskriva vem som får läsa varje typ av data, var kontrollen sker och hur du har testat att andra nekas. När prototypens tillfälliga regler ska ersättas med en hållbar behörighetsmodell är nästa steg att gå från prototyp till produkt.


Källa: Breakit

Vill du veta vad just ditt bygge skulle kräva?

Tre frågor, sen får du en klickbar skiss gratis.

← Alla artiklar