● CWE-862 · OWASP A01:2021

Zugriffsregeln in Supabase und Firebase (CWE-862): Wie fehlende Row Level Security Daten preisgibt und wie man sie behebt

Supabase- und Firebase-Apps lassen den Browser die Datenbank direkt mit einem öffentlichen Schlüssel abfragen, sodass zwischen einem Besucher und jeder Zeile nur Row Level Security oder Security Rules stehen. Eine per SQL angelegte Tabelle ohne enable row level security oder eine Firestore-Regel, die auf allow read, write: if true stehen geblieben ist, kann jeder lesen und ändern. Wie Richtlinien mit USING und WITH CHECK die Zeilen auf den angemeldeten Nutzer beschränken, mit einem PostgreSQL-Migrationsbeispiel.

Einfache Erklärung (ELI5)

Ein Bürogebäude gibt jedem Besucher dieselbe Lobby-Karte, aufgedruckt auf der Rückseite der Broschüre. Das ist in Ordnung, solange die Tür jeder Etage prüft, wer man ist, bevor sie aufgeht. Eine Etage wurde in Eile eingerichtet, und ihre Tür hat gar kein Lesegerät, also öffnet die Lobby-Karte sie, und jeder kann hinein und die Akten lesen. Der anon-Schlüssel von Supabase ist die Lobby-Karte: Er soll öffentlich sein. Row Level Security ist das Lesegerät an jeder Etagentür. Die Lösung: an jeder Tür ein Lesegerät anbringen und ihm genau sagen, wessen Akten jede Person sehen darf.

Kernkonzepte & Begriffe

Öffentlicher anon-Schlüssel
Der Supabase-Schlüssel anon und die Firebase-Webkonfiguration stecken im App-Bundle, und jeder kann sie kopieren. Sie identifizieren das Projekt, nicht den Nutzer, sind also kein Geheimnis und schützen Daten nicht allein.
Row Level Security (RLS)
Eine PostgreSQL-Funktion, die jede Abfrage durch Richtlinien filtert. Der Table Editor von Supabase aktiviert sie für neue Tabellen, doch per SQL oder Migration angelegte Tabellen starten ohne RLS, und die Standardrechte erlauben anon und authenticated dann, jede Zeile zu lesen und zu ändern.
USING und WITH CHECK
USING legt fest, welche vorhandenen Zeilen ein Nutzer sehen, ändern oder löschen darf; WITH CHECK legt fest, welche neuen oder geänderten Zeilen er schreiben darf. Eine Richtlinie nur mit USING bei insert oder update erlaubt, Zeilen für eine fremde Organisation zu schreiben.
service_role-Schlüssel
Der Schlüssel service_role umgeht RLS vollständig. Er gehört nur auf Server und in Edge Functions, nie in einen Browser oder eine mobile App.
Firestore-Regeln im Testmodus
Im Testmodus gestartete Projekte erhalten Regeln, die bis zu einem Datum alle Lese- und Schreibzugriffe erlauben, und viele werden später auf allow read, write: if true geändert. Regeln müssen request.auth.uid mit dem Besitzerfeld des Dokuments vergleichen.

Schritt-für-Schritt Angriffsablauf

Schritt 1

Der Angreifer öffnet die App

Supabase-URL und anon-Schlüssel stehen im JavaScript-Bundle oder im Paket der mobilen App.

Schritt 2

Er fragt die Tabelle direkt ab

Er ruft den REST-Endpunkt für invoices nur mit dem anon-Schlüssel auf und umgeht die Oberfläche der App. Ein Request an ein Testprojekt mit dem anon-Schlüssel und ohne Nutzersitzung zeigt den Fall.

Schritt 3

Die Datenbank liefert jede Zeile

RLS ist aus und anon hat select-Rechte, also filtert PostgreSQL nichts und liefert die Rechnungen aller Organisationen.

Schritt 4

Daten fließen ab oder werden verändert

Sind auch update und delete gewährt, kann der Angreifer Datensätze ändern oder löschen. 2025 wurde CVE-2025-48757 vergeben, nachdem Forscher fehlende RLS gefunden hatten, durch die Daten in mehr als 170 von einem KI-App-Baukasten erzeugten Apps offenlagen.

Quellcode: Verwundbar vs. Sicher

VERWUNDBARE IMPLEMENTIERUNG
-- migration.sql: RLS wird für diese Tabelle nie aktiviert
create table public.invoices (
  id bigint generated always as identity primary key,
  org_id uuid not null references public.orgs (id),
  customer text not null,
  amount_cents bigint not null
);

-- der öffentliche anon-Schlüssel darf jede Zeile lesen, ändern und löschen
grant select, insert, update, delete on public.invoices to anon, authenticated;
GEHÄRTETER SICHERHEITS-PATCH
-- migration.sql: RLS an, anon entzogen, eine Richtlinie pro Befehl
create table public.invoices (
  id bigint generated always as identity primary key,
  org_id uuid not null references public.orgs (id),
  customer text not null,
  amount_cents bigint not null
);

-- mit aktiver RLS gilt: keine Richtlinie, kein Zugriff
alter table public.invoices enable row level security;
revoke all on public.invoices from anon;

-- Nutzer sehen nur Rechnungen ihrer eigenen Organisationen
create policy "members read their org's invoices"
  on public.invoices for select to authenticated
  using (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));

-- neue Zeilen müssen zu einer Organisation des Nutzers gehören; keine update- oder delete-Richtlinie
create policy "members add invoices to their org"
  on public.invoices for insert to authenticated
  with check (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));

Checkliste für Engineering & Systemsicherheit

Quellen

← Zum vollständigen Sicherheitsverzeichnis Alle Anleitungen zu Schwachstellen →