TL;DR
Begge låser en ny tabell som standard: Appwrite svarte 401 til en gjest og til en innlogget bruker, og Supabase svarer 401/403 til du gir API-rollene grant på tabellen. Forskjellen vises når en regel er feil eller en forespørsel krysser brukere. På Supabase ga en oppdatering av en annen brukers rad 200 [], og en slurvete insert-policy lot en bruker bli med i et annet selskap; på Appwrite kom hvert forsøk på tvers av brukere tilbake som 401 eller 404, og en bruker kunne ikke gi raden sin til andre enn seg selv eller alle. Appwrite-servernøkler har scopes; en Supabase secret key hopper over alle policyer.
Jeg er Denys, fullstack- og AI-ingeniør. De fleste appene jeg bygger eller retter, kjører på Supabase, og klienter som fortsatt velger backend spør hvordan Appwrite står seg på sikkerhet. Funksjonslister svarer ikke på det, så jeg sendte de samme forespørslene til begge.
Slik testet jeg. Appwrite 2.3.0, selvhostet med Docker 30. oktober 2026, via REST-API-et som gjest, som to innloggede brukere A og B og med to servernøkler. Supabase-siden er min kjøring på en lokal stack (CLI 2.109.1, Postgres 17.6) fra 24. oktober 2026 med de samme rollene, beskrevet i sin helhet i Supabase RLS policy-eksempler. Oppdiktede rader på begge.
Hvordan beskytter Appwrite og Supabase data?
Supabase beskytter tabeller med Postgres: et grant avgjør om API-rollene i det hele tatt når en tabell, og row level security-policyer, skrevet i SQL, avgjør hvilke rader hver forespørsel ser og skriver. Appwrite lagrer tilganger ved siden av dataene: en tabell har en liste over roller som får lese, opprette, oppdatere eller slette, og med row security på har hver rad sin egen liste. I Appwrites dokumentasjon om tilganger blir tilgang bare gitt, aldri nektet, og en bruker trenger den enten på tabellnivå eller radnivå, ikke begge.
Spørsmålet er altså ikke hvilken som har tilgangskontroll. Begge har. Spørsmålet er hva hver av dem lar deg gjøre feil.
Hva gjør hver av dem med en ny tabell?
| Forespørsel | Appwrite, ingen tilganger satt | Supabase, RLS på, ingen grant (24. okt) |
|---|---|---|
| Gjest lister rader | 401 user_unauthorized |
401 permission denied |
| Innlogget bruker lister rader | 401 user_unauthorized |
403 permission denied |
| Innlogget bruker legger til en rad | 401 No permissions provided for action ‘create’ |
ikke testet |
En ny tabell er stengt på begge. På Supabase er det nytt: tabeller opprettet etter 30. oktober 2026 får ikke lenger Data API-grants automatisk, som jeg gikk gjennom i grant-endringen 30. oktober.
Hva skjer når en tabell er åpen for alle?
De to «åpne» innstillingene er ikke det samme. I Appwrite ga jeg tabellen én tilgang, read("any"). På Supabase er den vanlige åpne tilstanden RLS av med standardgrants fortsatt på plass.
| Forespørsel fra en gjest | Appwrite, read("any") |
Supabase, RLS av, standardgrants (24. okt) |
|---|---|---|
| List rader | 200, begge radene |
200, begge radene |
| Slett en rad | 401 |
200, begge radene slettet |
Appwrites «offentlig» betyr skrivebeskyttet med mindre du også gir skrivetilgang. En Supabase-tabell med RLS av kan leses og skrives av alle som har publishable key, og derfor er «RLS disabled in public» det første jeg sjekker.
Hvordan fungerer «egne rader» i hver av dem?
I Appwrite slo jeg på row security og ga tabellen én tilgang, create("users"). Da A opprettet en rad uten å oppgi tilganger, satte Appwrite dem selv: read, update og delete bare for bruker A. På Supabase er samme intensjon fire policyer på (select auth.uid()) = user_id.
| Forespørsel | Appwrite (30. okt) | Supabase (24. okt) |
|---|---|---|
| Gjest lister rader | 200, 0 rader |
401 (ingen grant til anon) |
| A lister rader | 200, A sine 2 rader |
200, A sine 2 rader |
| A leser Bs rad | 404 row_not_found |
returneres ikke i listen |
| A oppdaterer Bs rad | 401 |
200 [] |
| A sletter Bs rad | 401 |
200 [] |
Ingen av dem lekket Bs rad. Forskjellen er svaret. Supabase filtrerer bort rader en policy skjuler, så en oppdatering som ikke traff noe, er en suksess med null rader; en app som tar «ingen feil» for «lagret», forteller brukeren at endringen virket. Appwrite avviser med en feil appen ikke kan overse.
Kan en bruker gi dataene sine til noen andre?
Her går generert kode oftest galt, så jeg prøvde det på begge.
| Forespørsel fra A | Appwrite | Supabase (24. okt) |
|---|---|---|
| Opprett en rad som B kan lese | 401, Permissions must be one of: any, users, user:A… |
avhenger av policyen |
| Endre egen rad slik at B eier den | 401, samme melding |
403 med et fullstendig with check; 200 med with check (true) |
| Opprett en rad alle kan lese | 201, og en gjest listet den etterpå |
bare hvis en policy tillater det |
Appwrite begrenser hva en bruker kan gi til rollene hun selv har: any, users, sin egen bruker og sine egne team. Det stoppet trekket «gi raden min til noen andre» uten noen regel fra meg. Det hindret ikke A i å publisere sin egen rad for alle med read("any"). Hvis en rad aldri må bli offentlig, bør ikke klienten være den som setter tilgangene; opprett den fra en serverfunksjon i stedet.
På Supabase blir utfallet det policyen sier. Et fullstendig with check nektet flyttingen med 403; with check (true), som noen generatorer skriver, lot A flytte raden til Bs konto.
Kan en bruker bli med i andres selskap?
På Supabase er teamtilgang vanligvis en memberships-tabell pluss policyer. I kjøringen min lot insert-policyen mange guider foreslår, with check (user_id = auth.uid()), A legge seg selv til i selskap 2 med 201 Created og deretter lese payroll-prosjektet.
Appwrite har team innebygd. B opprettet et team; A prøvde å legge seg selv til som medlem og som eier. Begge forespørslene ga 404 team_not_found: A kunne ikke engang se teamet. Teammedlemskap avgjøres av serveren, ikke av en regel du skriver, så akkurat denne lekkasjen finnes ikke. Rader trenger fortsatt read("team:…")-tilganger for å deles, og dem setter du selv.
Hva åpner en servernøkkel?
| Nøkkel | Appwrite | Supabase (fra testen min av anon vs service role) |
|---|---|---|
| Full servernøkkel | 200, alle 4 rader fra begge brukerne |
alle 3 rader, RLS hoppes over |
Nøkkel med smal scope (users.read) |
401, missing scope rows.read |
ingen nøkler med scope |
En Supabase secret key tilsvarer service_role, som har BYPASSRLS; hva den åpner, står i anon key vs service role key. En Appwrite-nøkkel ignorerer også radtilganger, men bare for scopene den ble opprettet med, så en nøkkel til en jobb som administrerer brukere, kan ikke lese tabellene dine. Begge nøklene i en nettleserbundle er en full lekkasje av alt de når.
To mindre funn på Appwrite: en gjest og en innlogget bruker som ba om listen over tabeller, fikk begge 401 (kallet krever tables.read, som bare nøkler har), og read("users") betyr hvilken som helst innlogget konto, den samme fellen som auth.uid() is not null på Supabase når registrering er åpen.
Hvilken er tryggest?
Ingen av dem er trygg eller utrygg som standard; begge startet stengt. De skiller seg i hvor en feil kan gjemme seg:
| Spørsmål | Appwrite | Supabase |
|---|---|---|
| Hvor tilgangsreglene bor | Rolleliste på tabeller og rader | SQL-grants og policyer |
| En nektet skriving på tvers av brukere | Feil (401 / 404) |
200 med null rader for update og delete |
| Å gi rader til andre | Bare til roller brukeren har | Det policyen tillater |
| Regler på kolonneverdier, MFA-nivå | Ikke i tilgangene; krever serverkode | I policyen, f.eks. aal2 for sensitive rader |
| Servernøkler | Med scope | Secret key hopper over alle policyer |
Appwrites modell er smalere, og i kjøringen min nektet den smale modellen hvert triks på tvers av brukere uten at jeg skrev noe. Supabases modell er SQL, så den kan uttrykke regler Appwrite-tilganger ikke kan, som «bare med MFA» eller «bare rader fra selskaper jeg tilhører», og den kan også uttrykke feil regel. Hvis teamet ditt ikke kommer til å lese policyene, tilgir Appwrites standardinnstillinger mer. Hvis du trenger regler som avhenger av dataene, lar Supabase deg skrive dem, og da må de testes; metoden jeg bruker, står i hvordan du tester Supabase RLS.
Hvordan sjekker du et Supabase-prosjekt?
Den gratis Supabase-sikkerhetssjekken leser prosjektet ditt skrivebeskyttet og flagger tabeller med RLS av, policyer som alltid er sanne, skrivepolicyer for gjester og grants som gir mer enn policyene antyder. Hva Supabase dekker for deg og hva det overlater til deg, står i Er Supabase trygt?. Det finnes ingen Appwrite-motsvarighet i den; for Appwrite kjører du forespørslene over som to brukere. For Firebase kjørte jeg samme type angrep i Firebase security rules eksempler.
Begrensninger. Én selvhostet Appwrite 2.3.0 på en bærbar og én lokal Supabase-stack, seks dager imellom, oppdiktede rader. Appwrite Cloud kan avvike fra selvhostet. Storage, functions, realtime, auth-innstillinger og samsvarssertifikater er ikke sammenlignet her.
Velger du backend til en app med private data?
Jeg ser gjerne på tilgangsmodellen din sammen med deg. Ingen salgstale.
Ta kontakt