Přejít na obsah

Appwrite vs Supabase: bezpečnost, stejné útoky na oba

Zobrazit jako MarkdownOtevřít v ClaudeOtevřít v ChatGPTOtevřít v Perplexity

Otázka se zkopíruje také. Pokud se chat otevře prázdný, vložte ji.

TL;DR

Obojí ve výchozím stavu zamyká novou tabulku: Appwrite odpověděl 401 hostovi i přihlášenému uživateli a Supabase odpovídá 401/403, dokud tabulku nepřidělíte rolím API. Rozdíl se ukáže, když je pravidlo špatně nebo když požadavek přechází mezi uživateli. Na Supabase vrátila úprava cizího řádku 200 [] a příliš volná politika pro vložení nechala uživatele připojit se do jiné firmy; na Appwrite skončil každý pokus napříč uživateli v mém testu jako 401 nebo 404 a uživatel nemohl svůj řádek přidělit nikomu jinému než sobě nebo všem. Serverové klíče Appwrite mají scopes; secret key Supabase přeskakuje všechny politiky.

Jsem Denys, full-stack a AI inženýr. Většina aplikací, které stavím nebo opravuji, běží na Supabase, a klienti, kteří si backend teprve vybírají, se ptají, jak si Appwrite stojí v bezpečnosti. Seznam funkcí na to neodpoví, tak jsem oběma poslal stejné požadavky.

Jak jsem testoval. Appwrite 2.3.0, self-hosted v Dockeru, 30. října 2026, přes jeho REST API jako host, jako dva přihlášení uživatelé A a B a se dvěma serverovými klíči. Strana Supabase je můj test na lokálním stacku (CLI 2.109.1, Postgres 17.6) z 24. října 2026 se stejnými rolemi, podrobně popsaný v článku Příklady politik Supabase RLS. Na obou vymyšlené řádky.

Jak Appwrite a Supabase chrání data?

Supabase chrání tabulky přes Postgres: grant rozhoduje, zda se role API k tabulce vůbec dostanou, a politiky row level security, psané v SQL, rozhodují, které řádky každý požadavek vidí a zapisuje. Appwrite ukládá oprávnění vedle dat: tabulka má seznam rolí, které smějí číst, vytvářet, upravovat nebo mazat, a se zapnutou row security nese každý řádek vlastní seznam. Podle dokumentace oprávnění Appwrite se přístup jen uděluje, nikdy neodpírá, a uživatel ho potřebuje na úrovni tabulky nebo řádku, ne obojího.

Otázka tedy nezní, který z nich má řízení přístupu. Mají ho oba. Je to v tom, v čem vám každý dovolí udělat chybu.

Co dělá každý s novou tabulkou?

Požadavek Appwrite, bez nastavených oprávnění Supabase, RLS zapnuté, bez grantu (24. 10.)
Host vypíše řádky 401 user_unauthorized 401 permission denied
Přihlášený uživatel vypíše řádky 401 user_unauthorized 403 permission denied
Přihlášený uživatel přidá řádek 401 No permissions provided for action ‘create’ netestováno

Čerstvá tabulka je na obou uzavřená. Na Supabase je to novinka: tabulky vytvořené po 30. říjnu 2026 už automaticky nedostávají granty Data API, jak jsem popsal v článku změna grantů 30. října.

Co se stane, když je tabulka otevřená všem?

Dvě „otevřená“ nastavení nejsou totéž. V Appwrite jsem tabulce dal jediné oprávnění, read("any"). Na Supabase je běžný otevřený stav RLS vypnuté s výchozími granty.

Požadavek hosta Appwrite, read("any") Supabase, RLS vypnuté, výchozí granty (24. 10.)
Výpis řádků 200, oba řádky 200, oba řádky
Smazání řádku 401 200, oba řádky smazány

„Veřejné“ v Appwrite znamená jen čtení, dokud nepřidělíte i zápis. Tabulku Supabase s vypnutým RLS může číst i zapisovat kdokoli s publishable key, proto je „RLS disabled in public“ první věc, kterou kontroluji.

Jak fungují „vlastní řádky“ v každém?

V Appwrite jsem zapnul row security a tabulce dal jediné oprávnění, create("users"). Když A vytvořil řádek bez uvedení oprávnění, Appwrite je nastavil sám: read, update a delete jen pro uživatele A. Na Supabase je stejný záměr čtyři politiky na (select auth.uid()) = user_id.

Požadavek Appwrite (30. 10.) Supabase (24. 10.)
Host vypíše řádky 200, 0 řádků 401 (žádný grant pro anon)
A vypíše řádky 200, 2 řádky A 200, 2 řádky A
A čte řádek B 404 row_not_found ve výpisu se nevrátí
A upraví řádek B 401 200 []
A smaže řádek B 401 200 []
Stejný požadavek, dva backendy. Uživatel A upravuje řádek uživatele B přes REST API obou. Supabase, politika jen pro vlastníka: Uživatel A → úprava řádku B → 200 [] → 0 řádků, bez chyby; Appwrite, row security zapnuté: Uživatel A → úprava řádku B → 401 → vrácena chyba.
Obrázek 1. Moje testy: lokální stack Supabase (CLI 2.109.1), 24. října 2026; self-hosted Appwrite 2.3.0, 30. října 2026.

Ani jeden nevyzradil řádek B. Liší se odpověď. Supabase filtruje řádky, které politika skrývá, takže úprava, která nic nenašla, je úspěch s nulou řádků; aplikace, která bere „bez chyby“ jako „uloženo“, pak uživateli řekne, že jeho úprava prošla. Appwrite odmítne chybou, kterou aplikace nemůže přehlédnout.

Může uživatel předat svá data někomu jinému?

Tady generovaný kód chybuje nejčastěji, tak jsem to zkusil na obou.

Požadavek A Appwrite Supabase (24. 10.)
Vytvořit řádek čitelný uživatelem B 401, Permissions must be one of: any, users, user:A… záleží na politice
Změnit vlastní řádek, aby ho vlastnil B 401, stejná zpráva 403 s úplným with check; 200 s with check (true)
Vytvořit řádek čitelný všemi 201 a host ho pak vypsal jen pokud to politika dovolí

Appwrite omezuje, co může uživatel udělit, na role, které sám drží: any, users, svého uživatele a své týmy. To zablokovalo krok „dej můj řádek někomu jinému“, aniž bych napsal jediné pravidlo. Nezabránilo to A zveřejnit vlastní řádek všem přes read("any"). Pokud řádek nesmí být nikdy veřejný, neměl by oprávnění nastavovat klient; vytvořte ho ze serverové funkce.

Na Supabase je výsledek takový, jak říká politika. Úplný with check přesun odmítl s 403; with check (true), které píší někteří generátory, nechal A přesunout řádek na účet B.

Může se uživatel připojit do cizí firmy?

Na Supabase je přístup týmů obvykle tabulka memberships plus politiky. V mém testu insert politika, kterou navrhuje řada tutoriálů, with check (user_id = auth.uid()), nechala A přidat sebe do firmy 2 s 201 Created a pak číst její projekt s výplatami.

Appwrite má týmy zabudované. B vytvořil tým; A se do něj zkusil přidat jako člen i jako vlastník. Oba požadavky vrátily 404 team_not_found: A tým ani neviděl. O členství v týmu rozhoduje server, ne pravidlo, které píšete, takže tento konkrétní únik není možný. Řádky pořád potřebují oprávnění read("team:…"), aby se sdílely, a ta nastavujete sami.

Co otevírá serverový klíč?

Klíč Appwrite Supabase (z mého testu anon vs service role)
Plný serverový klíč 200, všechny 4 řádky obou uživatelů všechny 3 řádky, RLS přeskočeno
Klíč s úzkým scope (users.read) 401, missing scope rows.read žádné klíče se scope

Secret key Supabase odpovídá roli service_role, která má BYPASSRLS; co otevírá, je v článku anon key vs service role key. Klíč Appwrite také ignoruje oprávnění řádků, ale jen pro scopes, se kterými vznikl, takže klíč pro úlohu spravující uživatele nepřečte vaše tabulky. Jakýkoli klíč v balíčku pro prohlížeč je plný únik všeho, k čemu se dostane.

Dva menší nálezy v Appwrite: host i přihlášený uživatel, kteří žádali o seznam tabulek, dostali 401 (volání vyžaduje tables.read, které mají jen klíče), a read("users") znamená jakýkoli přihlášený účet, stejná past jako auth.uid() is not null na Supabase, když je registrace otevřená.

Který je bezpečnější?

Ani jeden není ve výchozím stavu bezpečný nebo nebezpečný; oba začaly uzavřené. Liší se tím, kde se může chyba schovat:

Otázka Appwrite Supabase
Kde žijí pravidla přístupu Seznamy rolí u tabulek a řádků SQL granty a politiky
Odepřený zápis napříč uživateli Chyba (401 / 404) 200 s nulou řádků u update a delete
Přidělování řádků jiným Jen rolím, které uživatel drží Cokoli, co politika dovolí
Pravidla podle hodnot sloupců, úroveň MFA Nejsou v oprávněních; chce serverový kód V politice, např. aal2 pro citlivé řádky
Serverové klíče Se scopes Secret key přeskakuje všechny politiky

Model Appwrite je užší a v mém testu odmítl každý trik napříč uživateli, aniž bych cokoli psal. Model Supabase je SQL, takže vyjádří pravidla, která oprávnění Appwrite neumí, třeba „jen s MFA“ nebo „jen řádky firem, do kterých patřím“, a vyjádří i špatné pravidlo. Pokud váš tým politiky číst nebude, Appwrite ve výchozím stavu odpouští víc. Pokud potřebujete pravidla závislá na datech, Supabase vám je dovolí napsat, a pak je nutné je testovat; metoda, kterou používám, je v článku jak testovat Supabase RLS.

Jak zkontrolovat projekt na Supabase?

Bezplatná Kontrola zabezpečení Supabase čte váš projekt jen pro čtení a označí tabulky s vypnutým RLS, politiky, které platí vždy, zápisové politiky pro hosty a granty, které dávají víc, než politiky naznačují. Co Supabase řeší za vás a co nechává na vás, je v článku je Supabase bezpečný?. Pro Appwrite nic takového není; spusťte výše uvedené požadavky jako dva uživatelé. Pro Firebase jsem stejný druh útoků pustil v článku příklady pravidel zabezpečení Firebase.

Limity. Jeden self-hosted Appwrite 2.3.0 na notebooku a jeden lokální stack Supabase, s odstupem šesti dnů, vymyšlené řádky. Appwrite Cloud se může lišit od self-hosted. Storage, funkce, realtime, nastavení autentizace a certifikáty shody se tu neporovnávají.

Vybíráte backend pro aplikaci se soukromými daty?

Rád se s vámi podívám na váš model přístupu. Bez nabídky.

Ozvěte se

© 2026 Denys Zaichenko. Původní článek: https://madsalt.tech/cs/blog/appwrite-vs-supabase/