Zum Inhalt springen

Appwrite vs Supabase Sicherheit: dieselben Angriffe

Als Markdown anzeigenIn Claude öffnenIn ChatGPT öffnenIn Perplexity öffnen

Die Frage wird ebenfalls kopiert. Falls der Chat leer öffnet, fügen Sie sie ein.

TL;DR

Beide sperren eine neue Tabelle standardmäßig: Appwrite antwortete einem Gast und einem angemeldeten Nutzer mit 401, Supabase antwortet mit 401/403, bis Sie die Tabelle den API-Rollen freigeben. Der Unterschied zeigt sich, wenn eine Regel falsch ist oder eine Anfrage Nutzergrenzen überschreitet. Bei Supabase lieferte ein Update auf die Zeile eines anderen 200 [], und eine zu lockere Insert-Policy ließ einen Nutzer einer fremden Firma beitreten; bei Appwrite kam jeder nutzerübergreifende Versuch in meinem Lauf als 401 oder 404 zurück, und ein Nutzer konnte seine Zeile nur sich selbst oder allen freigeben. Appwrite-Server-Keys haben Scopes; ein Supabase Secret Key umgeht jede Policy.

Ich bin Denys, Full-Stack- und KI-Entwickler. Die meisten Apps, die ich baue oder repariere, laufen auf Supabase, und Kunden, die noch ein Backend wählen, fragen, wie Appwrite bei der Sicherheit abschneidet. Featurelisten beantworten das nicht, also habe ich beiden dieselben Anfragen geschickt.

So habe ich getestet. Appwrite 2.3.0, selbst gehostet mit Docker am 30. Oktober 2026, über die REST-API als Gast, als zwei angemeldete Nutzer A und B und mit zwei Server-Keys. Die Supabase-Seite ist mein Lauf auf einem lokalen Stack (CLI 2.109.1, Postgres 17.6) vom 24. Oktober 2026 mit denselben Rollen, ausführlich beschrieben in Supabase RLS Policy Beispiele. Auf beiden Seiten ausgedachte Zeilen.

Wie schützen Appwrite und Supabase Daten?

Supabase schützt Tabellen mit Postgres: Ein Grant entscheidet, ob die API-Rollen eine Tabelle überhaupt erreichen, und Row-Level-Security-Policies, in SQL geschrieben, entscheiden, welche Zeilen jede Anfrage sieht und schreibt. Appwrite speichert Berechtigungen neben den Daten: Eine Tabelle hat eine Liste von Rollen, die lesen, anlegen, ändern oder löschen dürfen, und bei eingeschalteter Row Security trägt jede Zeile ihre eigene Liste. In den Appwrite-Berechtigungsdokumenten wird Zugriff nur gewährt, nie verweigert, und ein Nutzer braucht ihn auf Tabellen- oder Zeilenebene, nicht auf beiden.

Die Frage ist also nicht, wer Zugriffskontrolle hat. Beide haben sie. Die Frage ist, wo sich jeweils Fehler einschleichen können.

Was passiert mit einer neuen Tabelle?

Anfrage Appwrite, keine Berechtigungen gesetzt Supabase, RLS an, kein Grant (24. Okt.)
Gast listet Zeilen 401 user_unauthorized 401 permission denied
Angemeldeter Nutzer listet Zeilen 401 user_unauthorized 403 permission denied
Angemeldeter Nutzer fügt Zeile ein 401 No permissions provided for action ‘create’ nicht getestet

Eine frische Tabelle ist bei beiden geschlossen. Bei Supabase ist das neu: Tabellen, die nach dem 30. Oktober 2026 angelegt werden, bekommen nicht mehr automatisch Data-API-Grants; das habe ich in der Grant-Änderung vom 30. Oktober beschrieben.

Was passiert, wenn eine Tabelle für alle offen ist?

Die beiden „offenen“ Einstellungen sind nicht dasselbe. In Appwrite habe ich der Tabelle eine einzige Berechtigung gegeben, read("any"). Bei Supabase ist der übliche offene Zustand RLS aus, während die Standard-Grants bestehen bleiben.

Anfrage eines Gastes Appwrite, read("any") Supabase, RLS aus, Standard-Grants (24. Okt.)
Zeilen listen 200, beide Zeilen 200, beide Zeilen
Zeile löschen 401 200, beide Zeilen gelöscht

„Öffentlich“ heißt bei Appwrite nur lesbar, solange Sie nicht auch Schreibrechte vergeben. Eine Supabase-Tabelle mit ausgeschaltetem RLS ist für jeden mit dem Publishable Key les- und schreibbar. Deshalb prüfe ich „RLS disabled in public“ als Erstes.

Wie funktionieren „eigene Zeilen“ jeweils?

In Appwrite habe ich Row Security eingeschaltet und der Tabelle eine Berechtigung gegeben, create("users"). Als A eine Zeile ohne Berechtigungsangabe anlegte, setzte Appwrite sie selbst: read, update und delete nur für Nutzer A. Bei Supabase sind es für dieselbe Absicht vier Policies auf (select auth.uid()) = user_id.

Anfrage Appwrite (30. Okt.) Supabase (24. Okt.)
Gast listet Zeilen 200, 0 Zeilen 401 (kein Grant für anon)
A listet Zeilen 200, As 2 Zeilen 200, As 2 Zeilen
A liest Bs Zeile 404 row_not_found nicht in der Liste enthalten
A ändert Bs Zeile 401 200 []
A löscht Bs Zeile 401 200 []
Dieselbe Anfrage, zwei Backends. Nutzer A ändert eine Zeile von Nutzer B, über die jeweilige REST-API. Supabase, Policy nur für Besitzer: Nutzer A → Zeile von B ändern → 200 [] → 0 Zeilen, kein Fehler; Appwrite, Row Security an: Nutzer A → Zeile von B ändern → 401 → Fehler zurückgegeben.
Abbildung 1. Meine Tests: lokaler Supabase-Stack (CLI 2.109.1), 24. Oktober 2026; selbst gehostetes Appwrite 2.3.0, 30. Oktober 2026.

Keiner von beiden gab Bs Zeile preis. Der Unterschied ist die Antwort. Supabase filtert Zeilen heraus, die eine Policy verbirgt, daher ist ein Update ohne Treffer ein Erfolg mit null Zeilen; eine App, die „kein Fehler“ als „gespeichert“ versteht, sagt dem Nutzer, seine Änderung habe geklappt. Appwrite lehnt mit einem Fehler ab, den die App nicht übersehen kann.

Kann ein Nutzer seine Daten an jemand anderen übergeben?

Hier geht generierter Code am häufigsten schief, deshalb habe ich es bei beiden versucht.

Anfrage von A Appwrite Supabase (24. Okt.)
Zeile anlegen, die B lesen darf 401, Permissions must be one of: any, users, user:A… hängt von der Policy ab
Eigene Zeile so ändern, dass B sie besitzt 401, gleiche Meldung 403 mit vollständigem with check; 200 mit with check (true)
Zeile anlegen, die alle lesen dürfen 201, und ein Gast konnte sie danach listen nur wenn eine Policy es erlaubt

Appwrite beschränkt, was ein Nutzer vergeben kann, auf Rollen, die er selbst hat: any, users, den eigenen Nutzer und die eigenen Teams. Das blockierte den Zug „meine Zeile jemand anderem geben“, ohne dass ich eine Regel geschrieben hätte. Es hinderte A aber nicht daran, die eigene Zeile mit read("any") für alle zu veröffentlichen. Wenn eine Zeile nie öffentlich sein darf, sollte nicht der Client ihre Berechtigungen setzen; legen Sie sie stattdessen aus einer Server-Funktion an.

Bei Supabase ergibt sich, was die Policy sagt. Ein vollständiges with check lehnte den Zug mit 403 ab; with check (true), wie es manche Generatoren schreiben, ließ A die Zeile in Bs Konto verschieben.

Kann ein Nutzer der Firma eines anderen beitreten?

Bei Supabase besteht der Teamzugriff meist aus einer memberships-Tabelle plus Policies. In meinem Lauf ließ die Insert-Policy, die viele Tutorials vorschlagen, with check (user_id = auth.uid()), A sich selbst zu Firma 2 hinzufügen (201 Created) und danach deren Gehaltsprojekt lesen.

Appwrite hat Teams eingebaut. B legte ein Team an; A versuchte, sich als Mitglied und als Owner hinzuzufügen. Beide Anfragen lieferten 404 team_not_found: A konnte das Team nicht einmal sehen. Die Teammitgliedschaft entscheidet der Server, nicht eine Regel, die Sie schreiben, daher ist dieses Leck hier nicht möglich. Damit Zeilen geteilt werden, brauchen sie weiterhin read("team:…")-Berechtigungen, und die setzen Sie selbst.

Was öffnet ein Server-Key?

Key Appwrite Supabase (aus meinem Test Anon- vs. Service-Role-Key)
Voller Server-Key 200, alle 4 Zeilen beider Nutzer alle 3 Zeilen, RLS übersprungen
Key mit engem Scope (users.read) 401, missing scope rows.read keine Scope-Keys

Ein Supabase Secret Key entspricht service_role, und die hat BYPASSRLS; was sie öffnet, steht in Anon Key vs. Service-Role-Key. Auch ein Appwrite-Key ignoriert Zeilenberechtigungen, aber nur für die Scopes, mit denen er angelegt wurde; ein Key für einen Job, der Nutzer verwaltet, kann Ihre Tabellen also nicht lesen. Jeder der beiden Keys in einem Browser-Bundle ist ein vollständiges Leck dessen, was er erreicht.

Zwei kleinere Befunde zu Appwrite: Ein Gast und ein angemeldeter Nutzer, die die Liste der Tabellen abfragten, bekamen beide 401 (der Aufruf braucht tables.read, das nur Keys haben), und read("users") bedeutet jedes angemeldete Konto, dieselbe Falle wie auth.uid() is not null bei Supabase, wenn die Registrierung offen ist.

Welches ist sicherer?

Keines ist standardmäßig sicher oder unsicher; beide starteten geschlossen. Sie unterscheiden sich darin, wo sich ein Fehler verstecken kann:

Frage Appwrite Supabase
Wo die Zugriffsregeln liegen Rollenlisten an Tabellen und Zeilen SQL-Grants und Policies
Ein abgelehnter nutzerübergreifender Schreibzugriff Fehler (401 / 404) 200 mit null Zeilen bei Update und Delete
Zeilen an andere vergeben Nur an Rollen, die der Nutzer hat Was immer die Policy erlaubt
Regeln auf Spaltenwerte, MFA-Stufe Nicht in Berechtigungen; braucht Servercode In der Policy, z. B. aal2 für sensible Zeilen
Server-Keys Mit Scopes Secret Key überspringt alle Policies

Appwrites Modell ist enger, und in meinem Lauf wehrte das enge Modell jeden nutzerübergreifenden Trick ab, ohne dass ich etwas schreiben musste. Supabases Modell ist SQL, es kann also Regeln ausdrücken, die Appwrite-Berechtigungen nicht können, etwa „nur mit MFA“ oder „nur Zeilen der Firmen, in denen ich Mitglied bin“, und es kann ebenso die falsche Regel ausdrücken. Wenn Ihr Team die Policies nicht lesen wird, verzeihen Appwrites Standardwerte mehr. Wenn Sie Regeln brauchen, die von den Daten abhängen, lässt Supabase Sie sie schreiben, und dann müssen sie getestet werden; meine Methode steht in Supabase RLS testen.

Wie prüfen Sie ein Supabase-Projekt?

Der kostenlose Supabase-Sicherheitscheck liest Ihr Projekt nur lesend und meldet Tabellen mit ausgeschaltetem RLS, Policies, die immer wahr sind, Schreib-Policies für Gäste und Grants, die mehr geben, als die Policies vermuten lassen. Was Supabase für Sie abdeckt und was es Ihnen überlässt, steht in Wie sicher ist Supabase?. Ein Appwrite-Pendant gibt es darin nicht; bei Appwrite führen Sie die obigen Anfragen als zwei Nutzer aus. Für Firebase habe ich dieselbe Art von Angriffen in Firebase Security Rules Beispiele durchgespielt.

Grenzen. Ein selbst gehostetes Appwrite 2.3.0 auf einem Laptop und ein lokaler Supabase-Stack, sechs Tage auseinander, ausgedachte Zeilen. Appwrite Cloud kann sich vom selbst gehosteten unterscheiden. Storage, Functions, Realtime, Auth-Einstellungen und Compliance-Zertifikate sind hier nicht verglichen.

Ein Backend für eine App mit privaten Daten gesucht?

Gern schaue ich mit Ihnen auf Ihr Zugriffsmodell. Ohne Verkaufsgespräch.

Kontakt aufnehmen

© 2026 Denys Zaichenko. Originalartikel: https://madsalt.tech/de/blog/appwrite-vs-supabase/