PostgreSQL Row‑Level Security (RLS)‑Muster für Multi‑Tenant‑Apps
Lernen Sie PostgreSQL Row‑Level Security mit praktischen Mustern für Tenant‑Isolation und Rollenvorschriften, damit Zugriffe in der Datenbank durchgesetzt werden und nicht nur in der App.

Warum datenbankseitig durchgesetzte Zugriffe in Business‑Apps wichtig sind
Business‑Apps haben meist Regeln wie „Benutzer dürfen nur die Datensätze ihrer Firma sehen“ oder „nur Manager dürfen Rückerstattungen genehmigen“. Viele Teams setzen diese Regeln in der UI oder API durch und glauben, das reiche aus. Das Problem: Jeder zusätzliche Weg zur Datenbank erhöht das Risiko einer Datenveröffentlichung – ein internes Admin‑Tool, ein Hintergrundjob, eine Analytics‑Abfrage, ein vergessenes Endpoint oder ein Bug, der eine Prüfung überspringt.
Mandantenisolation bedeutet, dass ein Kunde (Tenant) niemals die Daten eines anderen Kunden lesen oder ändern kann, auch nicht versehentlich. Rollengestützte Zugriffe bedeuten, dass Personen innerhalb desselben Tenants unterschiedliche Rechte haben, zum Beispiel Agenten vs. Manager vs. Buchhaltung. Diese Regeln sind einfach zu beschreiben, aber schwer konsistent zu halten, wenn sie an vielen Stellen leben.
PostgreSQL Row‑Level Security (RLS) ist eine Datenbankfunktion, die der Datenbank erlaubt zu entscheiden, welche Zeilen eine Anfrage sehen oder ändern darf. Anstatt darauf zu hoffen, dass jede Abfrage im Code die richtige WHERE‑Klausel enthält, wendet die Datenbank die Policies automatisch an.
RLS ist kein Allheilmittel. Es entwirft nicht Ihr Schema, ersetzt nicht die Authentifizierung und schützt nicht vor jemandem, der bereits eine mächtige DB‑Rolle (z. B. Superuser) hat. Es verhindert auch nicht automatisch Logikfehler wie „jemand kann eine Zeile aktualisieren, die er nicht auswählen darf“, es sei denn, Sie schreiben Policies für Lesen und Schreiben.
Was Sie bekommen, ist ein starkes Sicherheitsnetz:
- Ein Regelset für jeden Codepfad, der die Datenbank berührt
- Weniger „Ups“‑Momente, wenn ein neues Feature deployed wird
- Klarere Audits, weil Zugriffsregeln in SQL sichtbar sind
- Besseren Schutz, falls ein API‑Bug durchrutscht
Es gibt einen kleinen Einrichtungsaufwand. Sie brauchen einen konsistenten Weg, „wer ist dieser Benutzer“ und „zu welchem Tenant gehört er“ in die Datenbank zu übergeben, und Sie müssen Policies pflegen, während Ihre App wächst. Die Rendite ist groß, besonders für SaaS und interne Tools mit sensiblen Kundendaten.
Row‑Level Security Grundlagen ohne Fachchinesisch
Row‑Level Security (RLS) filtert automatisch, welche Zeilen eine Abfrage sehen oder ändern darf. Anstatt sich auf jeden Screen, API‑Endpoint oder Report zu verlassen, „der sich an die Regeln erinnert“, wendet die Datenbank diese Regeln für Sie an.
Bei PostgreSQL schreiben Sie Policies, die bei jedem SELECT, INSERT, UPDATE und DELETE geprüft werden. Wenn die Policy sagt „dieser Benutzer darf nur Zeilen für Tenant A sehen“, dann gilt das auch für eine vergessene Admin‑Seite, eine neue Abfrage oder einen hastigen Hotfix.
RLS unterscheidet sich von GRANT/REVOKE. GRANT entscheidet, ob eine Rolle überhaupt auf eine Tabelle zugreifen darf (oder auf bestimmte Spalten). RLS entscheidet, welche Zeilen innerhalb dieser Tabelle erlaubt sind. In der Praxis nutzt man oft beides: GRANT, um zu begrenzen, wer Zugriff auf die Tabelle hat, und RLS, um zu begrenzen, welche Daten sie sehen oder ändern dürfen.
RLS hält sich auch in der realen, unordentlichen Welt. Views gehorchen normalerweise RLS, weil der Zugriff auf die zugrundeliegende Tabelle die Policy auslöst. Joins und Subqueries werden weiter gefiltert, sodass sich ein Benutzer nicht über Joins Zugang zu fremden Daten verschaffen kann. Und die Policy greift unabhängig davon, welcher Client die Abfrage ausführt: App‑Code, SQL‑Konsole, Hintergrundjob oder Reporting‑Tool.
RLS passt gut, wenn Sie starke Mandantenisolation benötigen, mehrere Wege haben, dieselben Daten abzufragen, oder viele Rollen Tabellen gemeinsam nutzen (üblich in SaaS und internen Tools). Für sehr kleine Apps mit einem vertrauenswürdigen Backend oder für nicht sensitive Daten kann RLS übertrieben sein. Sobald Sie mehr als einen Einstiegspunkt haben (Admin‑Tools, Exporte, BI, Skripte), zahlt sich RLS meist aus.
Beginnen Sie mit der Abbildung von Tenants, Rollen und Datenbesitz
Bevor Sie eine einzige Policy schreiben, stellen Sie klar, wem was gehört. PostgreSQL RLS funktioniert am besten, wenn Ihr Datenmodell bereits Tenants, Rollen und Besitzverhältnisse widerspiegelt.
Starten Sie mit Tenants. In den meisten SaaS‑Apps ist die einfachste Regel: Jede gemeinsame Tabelle mit Kundendaten hat eine tenant_id. Das gilt für offensichtliche Tabellen wie Rechnungen, aber auch für Dinge, die man gern vergisst: Anhänge, Kommentare, Audit‑Logs und Hintergrundjobs.
Nennen Sie dann die Rollen, die Menschen tatsächlich verwenden. Halten Sie die Menge klein und verständlich: owner, manager, agent, read‑only. Das sind Business‑Rollen, die Sie später in Policy‑Prüfungen abbilden (sie sind nicht dasselbe wie Datenbank‑Rollen).
Entscheiden Sie anschließend, wie Datensätze besessen werden. Manche Tabellen gehören einem einzelnen Benutzer (z. B. private Notizen). Andere sind team‑basiert (z. B. ein Shared Inbox). Beide ohne Plan zu mischen führt zu schwer lesbaren und leicht zu umgehenden Policies.
Ein einfacher Weg, Ihre Regeln zu dokumentieren, ist, für jede Tabelle die gleichen Fragen zu beantworten:
- Was ist die Tenant‑Grenze (welche Spalte erzwingt sie)?
- Wer darf Zeilen lesen (nach Rolle und Besitz)?
- Wer darf Zeilen erstellen und aktualisieren (und unter welchen Bedingungen)?
- Wer darf Zeilen löschen (normalerweise die strengste Regel)?
- Welche Ausnahmen sind erlaubt (Support‑Mitarbeiter, Automatisierung, Exporte)?
Beispiel: „Invoices“ könnte es Managern erlauben, alle Tenant‑Rechnungen zu sehen, Agenten Rechnungen für zugewiesene Kunden zu sehen und Read‑Only‑Benutzern nur Lesezugriff zu geben. Legen Sie vorher fest, welche Regeln strikt sein müssen (Tenant‑Isolation, Löschungen) und welche flexibel sein können (zusätzliche Sichtbarkeit für Manager). Wenn Sie mit einem No‑Code‑Tool wie AppMaster bauen, hilft diese Abbildung auch, UI‑Erwartungen und Datenbankregeln in Einklang zu halten.
Design‑Muster für Multi‑Tenant‑Tabellen
Multi‑Tenant‑RLS funktioniert am besten, wenn Ihre Tabellen vorhersehbar aussehen. Wenn jede Tabelle den Tenant anders speichert, werden Ihre Policies zu einem Puzzle. Eine konsistente Form macht PostgreSQL RLS leichter lesbar, testbar und langfristig korrekt zu halten.
Wählen Sie einen Tenant‑Identifier und verwenden Sie ihn überall. UUIDs sind üblich, weil sie schwer zu erraten und in vielen Systemen einfach zu generieren sind. Integer sind ebenfalls in Ordnung, besonders bei internen Apps. Slugs (wie „acme“) sind menschenfreundlich, können sich aber ändern – behandeln Sie sie daher als Anzeige‑Feld, nicht als Primärschlüssel.
Für tenant‑gebundene Daten fügen Sie jeder Tabelle, die zu einem Tenant gehört, eine tenant_id‑Spalte hinzu und setzen Sie möglichst NOT NULL. Wenn eine Zeile ohne Tenant existieren kann, ist das oft ein Geruchsmuster: Sie mischen globale und tenant‑spezifische Daten in einer Tabelle, was RLS‑Policies schwieriger und fragiler macht.
Indexierung ist einfach, aber wichtig. Die meisten SaaS‑Abfragen filtern zuerst nach Tenant, dann nach einem Geschäfts‑Feld wie Status oder Datum. Ein guter Default ist ein Index auf tenant_id; bei stark frequentierten Tabellen einen zusammengesetzten Index wie (tenant_id, created_at) oder (tenant_id, status) basierend auf Ihren häufigen Filtern.
Entscheiden Sie früh, welche Tabellen global sind und welche tenant‑gebunden. Häufige globale Tabellen sind Länder, Währungscodes oder Plandefinitionen. Tenant‑gebundene Tabellen sind Kunden, Rechnungen, Tickets und alles, was ein Tenant besitzt.
Wenn Sie wartbare Regeln wollen, halten Sie das Konzept eng:
- Tenant‑gebundene Tabellen:
tenant_id NOT NULL, RLS aktiviert, Policies prüfen immertenant_id. - Globale Referenztabellen: kein
tenant_id, keine Tenant‑Policies, für die meisten Rollen Read‑Only. - Gemeinsam genutzte, aber kontrollierte Tabellen: separate Tabellen pro Konzept (vermeiden Sie das Mischen von globalen und tenant‑Zeilen).
Wenn Sie mit einem Tool wie AppMaster arbeiten, zahlt sich diese Konsistenz auch im Datenmodell aus. Sobald tenant_id ein Standardfeld ist, können Sie dieselben Muster in mehreren Modulen ohne Überraschungen wiederverwenden.
Schritt für Schritt: Erstellen Sie Ihre erste Tenant‑Policy
Ein guter erster Erfolg mit PostgreSQL RLS ist eine einzelne Tabelle, die nur innerhalb des aktuellen Tenants lesbar ist. Der Punkt ist einfach: Selbst wenn jemand die WHERE‑Klausel in der API vergisst, verweigert die Datenbank Zeilen aus anderen Tenants.
Beginnen Sie mit einer Tabelle, die eine tenant_id‑Spalte enthält:
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
Sobald RLS aktiviert ist, überrascht das Verhalten manche: Wenn eine Rolle RLS‑unterworfen ist und keine passende Policy existiert, liefert SELECT null Zeilen (und Schreiboperationen schlagen fehl). Das ist am Anfang genau das, was Sie wollen.
Fügen Sie nun eine minimale Lese‑Policy hinzu. Dieses Beispiel geht davon aus, dass Ihre App nach dem Login eine Session‑Variable wie app.tenant_id setzt:
CREATE POLICY invoices_tenant_read
ON invoices
FOR SELECT
USING (tenant_id = current_setting('app.tenant_id')::uuid);
Als Nächstes fügen Sie Schreibregeln hinzu. In RLS steuert USING, welche vorhandenen Zeilen Sie berühren dürfen, und WITH CHECK, welche neuen Werte Sie schreiben dürfen.
CREATE POLICY invoices_tenant_insert
ON invoices
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY invoices_tenant_update
ON invoices
FOR UPDATE
USING (tenant_id = current_setting('app.tenant_id')::uuid)
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY invoices_tenant_delete
ON invoices
FOR DELETE
USING (tenant_id = current_setting('app.tenant_id')::uuid);
Policies sind standardmäßig PERMISSIVE, das heißt jede einzelne Policy kann Zugriff erlauben. Wählen Sie RESTRICTIVE, wenn alle Regeln bestehen müssen (nützlich, um eine zweite Absicherung wie „nur aktive Accounts“ hinzuzufügen).
Halten Sie Policies klein und zielgruppenorientiert. Statt einer riesigen Regel mit vielen OR‑Bedingungen, erstellen Sie separate Policies pro Publikum (z. B. invoices_tenant_read_app_user und invoices_tenant_read_support_agent). Das ist leichter zu testen, zu überprüfen und später sicherer zu ändern.
Tenant‑ und Benutzerkontext sicher übergeben
Damit PostgreSQL RLS funktioniert, muss die Datenbank wissen, „wer ruft auf“ und „zu welchem Tenant gehört die Person“. RLS‑Policies können nur Zeilen mit Werten vergleichen, die die Datenbank zur Abfragezeit lesen kann, also müssen Sie diesen Kontext in der Session setzen.
Ein verbreitetes Muster ist, nach der Authentifizierung Session‑Variablen zu setzen und die Policies current_setting() lesen zu lassen. Die App beweist die Identität (z. B. durch Verifizieren eines JWT) und schreibt dann Tenant‑ und Benutzer‑IDs in die DB‑Verbindung.
-- Pro Request (oder pro Transaktion) ausführen
SELECT set_config('app.tenant_id', '3f2a0c3e-9c7b-4d3f-9c5c-3c5e9c5d1a11', true);
SELECT set_config('app.user_id', '8d9c6b1a-6b6d-4e32-9c0d-2bfe6f6c1111', true);
SELECT set_config('app.role', 'support_agent', true);
-- In einer Policy
-- tenant_id Spalte ist ein UUID
USING (tenant_id = current_setting('app.tenant_id', true)::uuid);
Die dritte Argument true macht die Einstellung „lokal“ zur aktuellen Transaktion. Das ist wichtig bei Connection‑Pooling: Eine gepoolte Verbindung kann von einer anderen Anfrage wiederverwendet werden, daher möchten Sie nicht, dass der Tenant‑Kontext der vorherigen Anfrage hängen bleibt.
Kontext aus JWT‑Claims füllen
Wenn Ihre API JWTs nutzt, behandeln Sie Claims als Eingangsdaten, nicht als Wahrheit. Verifizieren Sie zuerst Signatur und Ablaufdatum des Tokens und kopieren Sie dann nur die nötigen Felder (tenant_id, user_id, role) in die Session‑Settings. Vermeiden Sie, dass Clients diese Werte direkt als Header oder Query‑Parameter senden.
Fehlender oder ungültiger Kontext: standardmäßig verweigern
Gestalten Sie Policies so, dass fehlende Einstellungen zu keiner Zeile führen.
Verwenden Sie current_setting('app.tenant_id', true), damit fehlende Werte NULL liefern. Casten Sie in den richtigen Typ (z. B. ::uuid), damit ungültige Formate schnell fehlschlagen. Und brechen Sie die Anfrage ab, wenn Tenant/Benutzer‑Kontext nicht gesetzt werden kann, statt einen Standard zu erraten.
So bleibt die Zugriffskontrolle konsistent, selbst wenn eine Abfrage die UI umgeht oder ein neuer Endpoint hinzugefügt wird.
Praktische Rollen‑Muster, die wartbar bleiben
Der einfachste Weg, RLS‑Policies lesbar zu halten, ist Identität von Berechtigungen zu trennen. Eine solide Basis ist eine users‑Tabelle plus eine memberships‑Tabelle, die einen Benutzer mit einem Tenant und einer Rolle (oder mehreren Rollen) verbindet. Dann können Policies eine Frage beantworten: „Hat der aktuelle Benutzer die richtige Mitgliedschaft für diese Zeile?“
Halten Sie Rollenbezeichnungen an realen Aktionen fest, nicht an Jobtiteln. invoice_viewer und invoice_approver altern besser als „manager“, weil die Policy sich in klaren Begriffen formulieren lässt.
Einige Rollen‑Muster, die mit der App wachsen, ohne kompliziert zu werden:
- Owner‑only: Die Zeile hat
created_by_user_id(oderowner_user_id) und der Zugriff prüft genau diesen Match. - Team‑only: Die Zeile hat
team_id, und die Policy prüft, ob der Benutzer Mitglied dieses Teams im selben Tenant ist. - Approved‑only: Lesen ist nur erlaubt, wenn
status = 'approved', und Schreiben ist auf Approver beschränkt. - Gemischte Regeln: Starten Sie strikt und fügen Sie dann kleine Ausnahmen hinzu (z. B. „Support kann lesen, aber nur innerhalb des Tenants“).
Cross‑Tenant‑Admins sind eine häufige Fehlerquelle. Behandeln Sie sie explizit, nicht als versteckten „Superuser“‑Shortcut. Schaffen Sie besser ein separates Konzept wie platform_admin (global) und verlangen Sie eine bewusste Prüfung in der Policy. Noch besser: Halten Sie Cross‑Tenant‑Zugriff standardmäßig schreibgeschützt und verlangen Sie für Schreibzugriffe eine höhere Hürde.
Dokumentation ist wichtiger, als es klingt. Fügen Sie über jeder Policy einen kurzen Kommentar hinzu, der die Absicht erklärt, nicht das SQL. „Approvers können den Status ändern. Viewers können nur genehmigte Rechnungen lesen.“ In sechs Monaten hilft diese Notiz, Policy‑Änderungen sicher vorzunehmen.
Wenn Sie mit einem No‑Code‑Tool wie AppMaster bauen, gelten diese Muster weiterhin. UI und API können schnell sein, aber die Datenbankregeln bleiben stabil, weil sie auf Mitgliedschaften und klaren Rollendefinitionen beruhen.
Beispiel‑Szenario: Ein einfaches SaaS mit Rechnungen und Support
Stellen Sie sich ein kleines SaaS vor, das mehrere Firmen bedient. Jede Firma ist ein Tenant. Die App hat Rechnungen (Geld) und Support‑Tickets (tägliche Hilfe). Benutzer können Agenten, Manager oder Support sein.
Datenmodell (vereinfacht): Jede Rechnung und jedes Ticket hat eine tenant_id. Tickets haben außerdem assignee_user_id. Die App setzt nach dem Login Tenant und Benutzer in der DB‑Session.
So reduziert RLS das tägliche Risiko:
Ein Benutzer aus Tenant A öffnet die Rechnungsliste und versucht, eine Rechnungs‑ID von Tenant B zu erraten (oder die UI sendet sie versehentlich). Die Abfrage läuft zwar, aber die Datenbank liefert null Zeilen, weil die Policy invoice.tenant_id = current_tenant_id verlangt. Es gibt keine „Zugriff verweigert“‑Leckage, nur ein leeres Ergebnis.
Innerhalb eines Tenants schärfen Rollen den Zugriff weiter. Ein Manager sieht alle Rechnungen und Tickets seines Tenants. Ein Agent sieht nur ihm zugewiesene Tickets und vielleicht seine Entwürfe. Hier passieren in APIs oft Fehler, insbesondere wenn Filter optional sind.
Support ist ein Sonderfall. Supportmitarbeiter müssen vielleicht Rechnungen einsehen, um Kunden zu helfen, sollten aber sensible Felder wie amount, bank_account oder tax_id nicht ändern dürfen. Ein praktikables Muster ist:
SELECTauf Rechnungen für die Support‑Rolle erlauben (tenant‑gegrenzt).UPDATEnur über einen „sicheren“ Pfad erlauben (z. B. eine View, die nur editierbare Spalten freigibt, oder eine strikte Update‑Policy, die Änderungen an geschützten Feldern ablehnt).
Im Szenario „versehentlicher API‑Bug“: Ein Endpoint vergisst beim Refactor den Tenant‑Filter. Ohne RLS kann das Cross‑Tenant‑Daten leaken. Mit RLS verweigert die Datenbank Zeilen außerhalb des Session‑Tenants, sodass der Bug zu einem defekten Bildschirm wird, nicht zu einer Datenpanne.
Wenn Sie dieses SaaS mit AppMaster bauen, sollten Sie diese Regeln trotzdem in der Datenbank haben. UI‑Prüfungen sind nützlich, aber die DB‑Regeln greifen, wenn etwas durchrutscht.
Häufige Fehler und wie man sie vermeidet
RLS ist mächtig, aber kleine Ausrutscher können „sicher“ in „überraschend“ verwandeln. Die meisten Probleme tauchen auf, wenn eine neue Tabelle hinzugefügt wird, eine Rolle geändert wird oder jemand mit dem falschen DB‑Benutzer testet.
Ein häufiger Fehler ist, RLS beim Anlegen einer neuen Tabelle zu vergessen. Sie schreiben sorgsame Policies für Kern‑Tabellen, fügen später eine „notes“‑ oder „attachments“‑Tabelle hinzu und shippen sie mit vollem Zugriff. Machen Sie es zur Gewohnheit: neue Tabelle == RLS aktivieren plus mindestens eine Policy.
Eine weitere Falle sind nicht übereinstimmende Policies über Aktionen hinweg. Eine Policy, die INSERT erlaubt, aber SELECT blockiert, kann dazu führen, dass Daten „verschwinden“, sobald sie erstellt wurden. Umgekehrt können Benutzer Zeilen lesen, die sie nicht erzeugen dürfen, und versuchen, in der UI Workarounds einzubauen. Denken Sie in Flows: „create then view“, „update then re‑open“, „delete then list“.
Seien Sie vorsichtig mit SECURITY DEFINER‑Funktionen. Sie laufen mit den Rechten des Funktions‑Owners und können RLS umgehen, wenn Sie nicht aufpassen. Wenn Sie sie nutzen, halten Sie sie klein, validieren Sie Eingaben und vermeiden Sie dynamisches SQL, sofern nicht unbedingt nötig.
Verlassen Sie sich nicht auf app‑seitige Filter, während der Datenbankzugriff offen bleibt. Selbst gut gebaute APIs erhalten neue Endpoints, Hintergrundjobs und Admin‑Skripte. Wenn die Datenbankrolle alles lesen kann, passiert das Leck früher oder später.
Um Probleme früh zu finden, halten Sie die Prüfungen praktisch:
- Testen Sie mit der gleichen DB‑Rolle, die Ihre Produktions‑App nutzt, nicht mit Ihrem persönlichen Admin‑User.
- Fügen Sie pro Tabelle einen Negativtest hinzu: ein Benutzer eines anderen Tenants muss null Zeilen sehen.
- Bestätigen Sie, dass jede Tabelle die erwarteten Aktionen unterstützt:
SELECT,INSERT,UPDATE,DELETE. - Überprüfen und dokumentieren Sie den Einsatz von
SECURITY DEFINER. - Nehmen Sie „RLS aktiviert?“ in Code‑Review‑Checklisten und Migrations auf.
Beispiel: Wenn ein Support‑Agent eine Rechnungskommentar erstellt, ihn aber nicht zurücklesen kann, liegt das oft an einer INSERT‑Policy ohne passende SELECT‑Policy (oder daran, dass der Tenant‑Kontext in dieser Session nicht gesetzt wurde).
Schnelle Checkliste, um Ihr RLS‑Setup zu validieren
RLS kann in Reviews korrekt aussehen und in der Praxis dennoch versagen. Validierung bedeutet weniger, Policies zu lesen, als sie mit realistischen Accounts und Abfragen zu durchbrechen. Testen Sie so, wie Ihre App die Daten nutzt, nicht wie Sie hoffen, dass sie es tut.
Erstellen Sie zunächst eine kleine Menge Testidentitäten. Nutzen Sie mindestens zwei Tenants (Tenant A und Tenant B). Für jeden Tenant fügen Sie einen normalen Benutzer und eine Admin‑/Manager‑Rolle hinzu. Wenn Sie Support‑Agenten oder Read‑Only‑Rollen unterstützen, fügen Sie auch diese hinzu.
Belasten Sie RLS dann mit einem kleinen, wiederholbaren Satz von Checks:
- Führen Sie die Kernoperationen für jede Rolle aus: Listen, Einzelabruf per ID, Insert, Update und Delete. Testen Sie jeweils erlaubte und geblockte Fälle.
- Beweisen Sie Tenant‑Grenzen: Versuchen Sie als Tenant A, Daten von Tenant B zu lesen oder zu ändern. Sie sollten null Zeilen oder einen Berechtigungsfehler erhalten, niemals teilweise Ergebnisse.
- Testen Sie Joins auf Lecks: Joins geschützter Tabellen mit anderen Tabellen (inklusive Lookup‑Tabellen). Bestätigen Sie, dass ein Join nicht dazu führt, dass verwandte Zeilen eines anderen Tenants hereingezogen werden.
- Prüfen Sie, dass fehlender oder falscher Kontext den Zugriff verweigert: löschen Sie den Tenant/Benutzer‑Kontext und wiederholen Sie. „Kein Kontext“ sollte geschlossen fehlschlagen. Testen Sie auch eine ungültige Tenant‑ID.
- Bestätigen Sie die Basis‑Performance: Schauen Sie sich Query‑Pläne und Indexnutzung in Bezug auf Ihren Tenant‑Filter (
tenant_idplus Sortier‑/Suchfelder) an.
Wenn ein Test überrascht, beheben Sie zuerst die Policy oder das Context‑Setting. Patchen Sie es nicht in der UI und hoffen, dass die DB‑Regeln schon halten.
Nächste Schritte: Sicher ausrollen und Konsistenz wahren
Behandeln Sie PostgreSQL RLS wie ein Sicherheitssystem: führen Sie es vorsichtig ein, prüfen Sie es regelmäßig und halten Sie die Regeln einfach genug, damit Ihr Team sie einhält.
Starten Sie klein. Wählen Sie die Tabellen, bei denen ein Leak am schmerzhaftesten wäre (Zahlungen, Rechnungen, HR‑Daten, Kundenkommunikation) und aktivieren Sie dort zuerst RLS. Frühe Erfolge sind besser als ein großer Rollout, den niemand vollständig versteht.
Eine sinnvolle Rollout‑Reihenfolge sieht oft so aus:
- Kern‑„owned“ Tabellen zuerst (Zeilen gehören eindeutig einem Tenant)
- Tabellen mit personenbezogenen Daten (PII)
- Tabellen, die geteilt, aber tenant‑gefiltert sind (Reports, Analytics)
- Join‑Tabellen und Randfälle (Many‑to‑Many‑Beziehungen)
- Alles andere, wenn die Grundlagen stabil sind
Machen Sie Tests verpflichtend. Automatisierte Tests sollten dieselben Abfragen als verschiedene Tenants und Rollen ausführen und bestätigen, was erlaubt bzw. verweigert wird. Einschließen Sie sowohl „sollte erlauben“ als auch „sollte verweigern“, denn die teuersten Bugs sind stille Übererlaubnisse.
Setzen Sie den Session‑Kontext an genau einem Ort im Request‑Flow, bevor irgendwelche Abfragen laufen. Tenant‑ID, User‑ID und Rolle sollten einmalig, früh und nicht später geraten gesetzt werden. Wenn Sie den Kontext mitten in einer Transaktion setzen, führen Sie früher oder später eine Abfrage mit fehlenden oder veralteten Werten aus.
Wenn Sie mit AppMaster arbeiten, planen Sie Konsistenz zwischen den generierten Backend‑APIs und Ihren PostgreSQL‑Policies. Standardisieren Sie, wie Tenant‑ und Rollen‑Kontext in die Datenbank gelangt (z. B. dieselben Session‑Variablen für jeden Endpoint), damit Policies überall gleich wirken. Wenn Sie AppMaster bei appmaster.io verwenden, ist RLS weiterhin die letztinstanzliche Autorität für Tenant‑Isolation, auch wenn Sie Zugriffe zusätzlich in der UI einschränken.
Schließlich: Achten Sie darauf, was fehlschlägt. Autorisierungsfehler sind nützliche Signale, besonders kurz nach dem Rollout. Verfolgen Sie wiederholte Verweigerungen und untersuchen Sie, ob sie auf einen echten Angriff, einen kaputten Client‑Flow oder eine zu strenge Policy hinweisen.
Eine kurze Gewohnheitsliste, die RLS gesund hält:
- Default‑deny‑Denkweise, Ausnahmen bewusst hinzufügen
- Klare Policy‑Namen (Tabelle + Aktion + Publikum)
- Policy‑Änderungen wie Code‑Änderungen reviewen
- Verweigerungen während des frühen Rollouts protokollieren und prüfen
- Für jede neue Tabelle mit RLS eine kleine Testmenge anlegen


