---
title: "Appwrite vs Supabase tietoturva: samat hyökkäykset"
description: "Samat pääsyhyökkäykset Appwrite 2.3.0:aa ja paikallista Supabasea vastaan. Molemmat lukitsevat uudet taulut; ero on virheen näkyvyydessä ja avaimen oikeuksissa."
url: "https://madsalt.tech/fi/blog/appwrite-vs-supabase/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/fi/about/"
language: "fi"
published: 2026-10-30
---

# Appwrite vs Supabase tietoturva: samat hyökkäykset

TL;DR

Molemmat lukitsevat uuden taulun oletuksena: Appwrite vastasi `401` sekä vieraalle että kirjautuneelle käyttäjälle, ja Supabase vastaa `401`/`403`, kunnes annat API-rooleille grantin tauluun. Ero näkyy, kun sääntö on väärin tai pyyntö ylittää käyttäjärajan. Supabasessa toisen käyttäjän rivin update palautti `200 []`, ja huolimaton insert-politiikka antoi käyttäjän liittyä toiseen yritykseen; Appwritessa jokainen käyttäjärajan ylittänyt yritys palasi vastauksella `401` tai `404`, eikä käyttäjä voinut antaa riviään muille kuin itselleen tai kaikille. Appwriten palvelinavaimilla on scopet; Supabasen secret key ohittaa jokaisen politiikan.

Olen Denys, full-stack- ja tekoälyinsinööri. Useimmat rakentamani tai korjaamani sovellukset pyörivät Supabasella, ja asiakkaat, jotka vielä valitsevat taustajärjestelmää, kysyvät, miten Appwrite pärjää tietoturvassa. Ominaisuuslistat eivät vastaa siihen, joten lähetin molemmille samat pyynnöt.

**Näin testasin.** Appwrite 2.3.0, itse ylläpidettynä Dockerilla 30. lokakuuta 2026, sen REST API:n kautta vieraana, kahtena kirjautuneena käyttäjänä A ja B sekä kahdella palvelinavaimella. Supabase-puoli on oma ajoni paikallisella stackilla (CLI 2.109.1, Postgres 17.6) 24. lokakuuta 2026 samoilla rooleilla, kuvattu kokonaan artikkelissa [Supabase RLS policy -esimerkit](https://madsalt.tech/fi/blog/supabase-rls-policy-examples/). Keksittyjä rivejä molemmissa.

## Miten Appwrite ja Supabase suojaavat dataa?

Supabase suojaa taulut Postgresilla: grant ratkaisee, yltävätkö API-roolit tauluun lainkaan, ja row level security -politiikat, jotka kirjoitetaan SQL:llä, ratkaisevat, mitkä rivit kukin pyyntö näkee ja kirjoittaa. Appwrite tallentaa oikeudet datan viereen: taululla on lista rooleista, jotka saavat lukea, luoda, päivittää tai poistaa, ja kun row security on päällä, jokaisella rivillä on oma listansa. [Appwriten oikeuksien dokumentaation](https://appwrite.io/docs/products/databases/tablesdb/permissions) mukaan pääsy vain annetaan, sitä ei koskaan evätä, ja käyttäjä tarvitsee sen joko taulu- tai rivitasolla, ei molemmilla.

Kysymys ei siis ole siitä, kummassa on pääsynhallinta. Molemmissa on. Kysymys on siitä, mitä kumpikin antaa sinun tehdä väärin.

## Mitä kumpikin tekee uudelle taululle?

| Pyyntö                            | Appwrite, oikeuksia ei asetettu                   | Supabase, RLS päällä, ei grantia (24.10.) |
| --------------------------------- | ------------------------------------------------- | ----------------------------------------- |
| Vieras listaa rivit               | `401` user_unauthorized                           | `401` permission denied                   |
| Kirjautunut käyttäjä listaa rivit | `401` user_unauthorized                           | `403` permission denied                   |
| Kirjautunut käyttäjä lisää rivin  | `401` No permissions provided for action 'create' | ei testattu                               |

Uusi taulu on molemmissa suljettu. Supabasessa se on uutta: 30. lokakuuta 2026 jälkeen luodut taulut eivät enää saa Data API -granteja automaattisesti, minkä kävin läpi artikkelissa [grant-muutos 30. lokakuuta](https://madsalt.tech/fi/blog/supabase-october-30-grant-change/).

## Mitä tapahtuu, kun taulu on auki kaikille?

Kaksi "avointa" asetusta eivät ole sama asia. Appwritessa annoin taululle yhden oikeuden, `read("any")`. Supabasessa tavallinen avoin tila on RLS pois päältä ja oletusgrantit paikallaan.

| Vieraan pyyntö | Appwrite, `read("any")` | Supabase, RLS pois, oletusgrantit (24.10.) |
| -------------- | ----------------------- | ------------------------------------------ |
| Listaa rivit   | `200`, molemmat rivit   | `200`, molemmat rivit                      |
| Poista rivi    | `401`                   | `200`, molemmat rivit poistettu            |

Appwriten "julkinen" tarkoittaa vain luku -tilaa, ellet anna myös kirjoitusoikeutta. Supabase-taulun, jolla RLS on pois, voi lukea ja kirjoittaa kuka tahansa, jolla on publishable key, ja siksi "RLS disabled in public" on ensimmäinen asia, jonka tarkistan.

## Miten "omat rivit" toimivat kummassakin?

Appwritessa laitoin row securityn päälle ja annoin taululle yhden oikeuden, `create("users")`. Kun A loi rivin määrittämättä oikeuksia, Appwrite asetti ne itse: `read`, `update` ja `delete` vain käyttäjälle A. Supabasessa sama tarkoitus on neljä politiikkaa ehdolla `(select auth.uid()) = user_id`.

| Pyyntö                | Appwrite (30.10.)   | Supabase (24.10.)                  |
| --------------------- | ------------------- | ---------------------------------- |
| Vieras listaa rivit   | `200`, 0 riviä      | `401` (ei grantia roolille `anon`) |
| A listaa rivit        | `200`, A:n 2 riviä  | `200`, A:n 2 riviä                 |
| A lukee B:n rivin     | `404` row_not_found | ei palaudu listassa                |
| A päivittää B:n rivin | `401`               | `200 []`                           |
| A poistaa B:n rivin   | `401`               | `200 []`                           |

![Sama pyyntö, kaksi taustajärjestelmää. Käyttäjä A muokkaa käyttäjän B omistamaa riviä kummankin REST API:n kautta. Supabase, omistajapolitiikka: Käyttäjä A → päivittää B:n rivin → 200 \[\] → 0 riviä, ei virhettä; Appwrite, row security päällä: Käyttäjä A → päivittää B:n rivin → 401 → virhe palautetaan.](https://madsalt.tech/charts/fi/appwrite-vs-supabase.svg)

*Kuva 1. Omat testini: paikallinen Supabase-stack (CLI 2.109.1), 24. lokakuuta 2026; itse ylläpidetty Appwrite 2.3.0, 30. lokakuuta 2026.*

Kumpikaan ei vuotanut B:n riviä. Ero on vastauksessa. Supabase suodattaa pois rivit, jotka politiikka piilottaa, joten update, joka ei osunut mihinkään, on onnistuminen nollalla rivillä; sovellus, joka pitää "ei virhettä" merkkinä "tallennettu", kertoo käyttäjälle, että muutos meni läpi. Appwrite kieltäytyy virheellä, jota sovellus ei voi ohittaa.

## Voiko käyttäjä antaa datansa jollekin toiselle?

Tähän generoitu koodi sortuu useimmin, joten kokeilin sitä molemmilla.

| A:n pyyntö                              | Appwrite                                               | Supabase (24.10.)                                                       |
| --------------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------- |
| Luo rivi, jonka B voi lukea             | `401`, Permissions must be one of: any, users, user:A… | riippuu politiikasta                                                    |
| Muuta oma rivi niin, että B omistaa sen | `401`, sama viesti                                     | `403` täydellä `with check` -ehdolla; `200` ehdolla `with check (true)` |
| Luo rivi, jonka kaikki voivat lukea     | `201`, ja vieras listasi sen sen jälkeen               | vain jos politiikka sallii                                              |

Appwrite rajoittaa, mitä käyttäjä voi antaa: vain rooleille, jotka hänellä itsellään on, eli `any`, `users`, hänen oma käyttäjänsä ja hänen omat tiiminsä. Se esti siirron "anna rivini jollekin toiselle" ilman, että kirjoitin mitään sääntöä. Se ei estänyt A:ta julkaisemasta omaa riviään kaikille `read("any")`-oikeudella. Jos rivin ei koskaan saa olla julkinen, asiakkaan ei pidä olla se, joka asettaa sen oikeudet; luo rivi palvelinfunktiosta.

Supabasessa lopputulos on se, mitä politiikka sanoo. Täydellinen `with check` esti siirron vastauksella `403`; `with check (true)`, jonka jotkin generaattorit kirjoittavat, antoi A:n siirtää rivin B:n tilille.

## Voiko käyttäjä liittyä jonkun toisen yritykseen?

Supabasessa tiimipääsy on yleensä `memberships`-taulu plus politiikat. Ajossani insert-politiikka, jota monet oppaat ehdottavat, `with check (user_id = auth.uid())`, antoi A:n lisätä itsensä yritykseen 2 vastauksella `201 Created` ja lukea sen payroll-projektin.

Appwritessa tiimit ovat sisäänrakennettuja. B loi tiimin; A yritti lisätä itsensä siihen jäsenenä ja omistajana. Molemmat pyynnöt palauttivat `404 team_not_found`: A ei nähnyt tiimiä lainkaan. Tiimin jäsenyyden ratkaisee palvelin, ei sääntö, jonka kirjoitat, joten juuri tämä vuoto ei ole mahdollinen. Rivit tarvitsevat silti `read("team:…")`-oikeudet tullakseen jaetuiksi, ja ne asetat itse.

## Mitä palvelinavain avaa?

| Avain                                  | Appwrite                                     | Supabase (anon vs service role -testistäni) |
| -------------------------------------- | -------------------------------------------- | ------------------------------------------- |
| Täysi palvelinavain                    | `200`, kaikki 4 riviä molemmilta käyttäjiltä | kaikki 3 riviä, RLS ohitetaan               |
| Avain kapealla scopella (`users.read`) | `401`, missing scope rows.read               | ei scope-avaimia                            |

Supabasen secret key vastaa roolia `service_role`, jolla on `BYPASSRLS`; mitä se avaa, kerrotaan artikkelissa [anon key vs service role key](https://madsalt.tech/fi/blog/supabase-anon-key-vs-service-role-key/). Myös Appwriten avain ohittaa rivioikeudet, mutta vain niiden scopejen osalta, joilla se luotiin, joten käyttäjiä hallinnoivan tehtävän avain ei voi lukea taulujasi. Kumpi tahansa avain selaimen bundlessa on täysi vuoto kaikesta, mihin se yltää.

Kaksi pienempää löydöstä Appwritessa: vieras ja kirjautunut käyttäjä, jotka pyysivät taulujen listaa, saivat molemmat `401` (kutsu vaatii `tables.read`-scopen, joka on vain avaimilla), ja `read("users")` tarkoittaa mitä tahansa kirjautunutta tiliä, sama ansa kuin `auth.uid() is not null` Supabasessa, kun rekisteröityminen on auki.

## Kumpi on turvallisempi?

Kumpikaan ei ole oletuksena turvallinen tai turvaton; molemmat alkoivat suljettuina. Ne eroavat siinä, missä virhe voi piillä:

| Kysymys                             | Appwrite                              | Supabase                                             |
| ----------------------------------- | ------------------------------------- | ---------------------------------------------------- |
| Missä pääsysäännöt asuvat           | Roolilistat tauluilla ja riveillä     | SQL-grantit ja politiikat                            |
| Evätty kirjoitus käyttäjien välillä | Virhe (`401` / `404`)                 | `200` ja nolla riviä updatessa ja deletessä          |
| Rivien antaminen muille             | Vain rooleille, jotka käyttäjällä on  | Mitä politiikka sallii                               |
| Säännöt sarakearvoista, MFA-taso    | Ei oikeuksissa; vaatii palvelinkoodia | Politiikassa, esim. `aal2` arkaluonteisille riveille |
| Palvelinavaimet                     | Scopella rajatut                      | Secret key ohittaa kaikki politiikat                 |

Appwriten malli on kapeampi, ja ajossani kapea malli esti jokaisen käyttäjärajan ylittävän kikan ilman, että kirjoitin mitään. Supabasen malli on SQL, joten se pystyy ilmaisemaan sääntöjä, joita Appwriten oikeudet eivät pysty, kuten "vain MFA:lla" tai "vain niiden yritysten rivit, joihin kuulun", ja se pystyy ilmaisemaan myös väärän säännön. Jos tiimisi ei aio lukea politiikkoja, Appwriten oletukset antavat enemmän anteeksi. Jos tarvitset sääntöjä, jotka riippuvat datasta, Supabase antaa kirjoittaa ne, ja silloin ne on testattava; käyttämäni menetelmä on artikkelissa [Supabase RLS:n testaus](https://madsalt.tech/fi/blog/how-to-test-supabase-rls/).

## Miten Supabase-projektin tarkistaa?

Ilmainen [Supabasen tietoturvatarkistus](https://madsalt.tech/fi/tools/supabase-security-check/) lukee projektisi vain luku -tilassa ja liputtaa taulut, joilla RLS on pois, aina tosi olevat politiikat, vieraiden kirjoituspolitiikat ja grantit, jotka antavat enemmän kuin politiikat antavat ymmärtää. Mitä Supabase hoitaa puolestasi ja mitä se jättää sinulle, kerrotaan artikkelissa [onko Supabase turvallinen?](https://madsalt.tech/fi/blog/is-supabase-secure/). Siinä ei ole Appwrite-vastinetta; Appwritelle aja yllä olevat pyynnöt kahtena käyttäjänä. Firebasella ajoin saman tyyppisiä hyökkäyksiä artikkelissa [Firebasen tietoturvasääntöjen esimerkit](https://madsalt.tech/fi/blog/firebase-security-rules-examples/).

**Rajoitukset.** Yksi itse ylläpidetty Appwrite 2.3.0 kannettavalla ja yksi paikallinen Supabase-stack, kuuden päivän välein, keksittyjä rivejä. Appwrite Cloud voi poiketa itse ylläpidetystä. Storagea, functionseja, realtimea, auth-asetuksia ja vaatimustenmukaisuussertifikaatteja ei vertailla tässä.

Valitsetko taustajärjestelmää sovellukselle, jossa on yksityistä dataa?

Katson mielelläni pääsymalliasi kanssasi. Ei myyntipuhetta.

[Ota yhteyttä](https://madsalt.tech/fi/#contact)
