Zum Inhalt springen
Webentwicklung

PostgreSQL Row-Level Security: Tenant-Isolierung ohne

Row-Level Security in PostgreSQL ermöglicht saubere Mandantentrennung auf Datenbankebene. Wie Sie RLS-Policies so konfigurieren, dass auch bei Millionen Zeilen kein messbarer Query-Slowdown entsteht.

Von Maik Boche

PostgreSQL Row-Level Security: Tenant-Isolierung ohne

PostgreSQL Row-Level Security (RLS) erzwingt Tenant-Isolierung direkt auf Datenbankebene: Selbst wenn Anwendungscode fehlerhafte WHERE-Klauseln enthält, verhindert die Datenbank automatisch den Zugriff auf mandantenfremde Zeilen. Für mittelständische E-Commerce-Betreiber mit Multi-Tenant-Architektur ist Datenisolierung keine Option, sondern Pflicht. Ein einziger Bug, der Kundendaten eines Mandanten einem anderen sichtbar macht, kann das Geschäftsmodell gefährden. RLS verlagert den Schutz von der Anwendungsschicht in die Datenbank: Policies greifen bei SELECT, INSERT, UPDATE und DELETE. Tabellen ohne Policy erhalten automatisch eine Default-Deny-Regel. Das reduziert die Angriffsfläche auch bei wachsenden Datenmengen im Millionenbereich nachhaltig.

PostgreSQL Row-Level Security: Tenant-Isolierung ohne Query-Slowdown bei Millionen Zeilen

Leitfakt: PostgreSQL Row-Level Security (RLS) erzwingt Datenisolierung direkt im Datenbankmotor. Laut PostgreSQL-Dokumentation schränken Row Security Policies auf Benutzerebene ein, welche Zeilen bei normalen Abfragen zurückgegeben oder durch Datenmodifikationsbefehle verändert werden dürfen. Ist RLS auf einer Tabelle aktiviert, gilt standardmäßig eine Deny-All-Policy, sofern keine explizite Policy definiert ist.


Warum applikationsseitige Isolierung allein riskant ist

Traditionelle Multi-Tenant-Architekturen verlassen sich auf Filterung im Anwendungscode, zum Beispiel SELECT * FROM invoices WHERE tenant_id = $1. OneUptime beschreibt das zentrale Problem: Vergisst ein Entwickler die WHERE-Klausel, liefert die Abfrage Daten aller Mandanten. Mit RLS hingegen erzwingt die Datenbank die Isolation automatisch, auch wenn der Anwendungscode fehlerhaft ist.

Für B2B-Kundenportale mit Rollen und Freigaben oder Shared-Catalog-Architekturen bedeutet das: Ein einzelner Implementierungsfehler im Anwendungscode führt nicht mehr automatisch zu mandantenübergreifenden Datenlecks.


Praktische Implikationen für Entscheider

1. Aktivierung und Policy-Definition sind zwei separate Schritte

Crunchy Data beschreibt das Vorgehen konkret: Zuerst wird RLS auf der Tabelle aktiviert (ALTER TABLE events ENABLE ROW LEVEL SECURITY), anschließend wird eine Policy definiert, die den Zugriff anhand von Bedingungen wie org_id oder Datenbankrollen einschränkt. Ohne eine explizit definierte Policy greift laut PostgreSQL-Dokumentation ein Default-Deny, das heißt keine Zeile ist sichtbar oder veränderbar. Betreiber von B2B-Onlineshops mit Firmenkunden, Rollen und Preisen müssen diese Konfiguration für jede relevante Tabelle separat und vollständig durchführen.

2. Tabellenbesitzer sind standardmäßig von RLS ausgenommen

Die PostgreSQL-Dokumentation hält fest: Der Tabellenbesitzer ist typischerweise nicht durch Row Security Policies eingeschränkt. Für produktive Multi-Tenant-Systeme bedeutet das, dass Anwendungsverbindungen nicht als Tabellenbesitzer hergestellt werden sollten. Datenbankarchitekten, die etwa ERP-Shop-Integrationen mit gemeinsamen Datenbanknutzern betreiben, sollten diesen Punkt explizit in ihrer Sicherheitsarchitektur adressieren.

3. Performance: Indexdesign bestimmt ob RLS skaliert

Bei Millionen Zeilen ist die Policy-Expression die entscheidende Variable. OneUptime empfiehlt, die in der Policy-Bedingung verwendeten Spalten, typischerweise tenant_id oder org_id, mit geeigneten Indizes zu versehen. Ohne passende Indizes führt RLS zu Full-Table-Scans für jede Abfrage. Für Systeme mit Echtzeit-Bestandsabfragen oder dynamischen Lieferzeitprognosen ist dieser Punkt betriebskritisch.

4. RLS gilt nicht für alle Operationen

Laut PostgreSQL-Dokumentation sind Operationen, die die gesamte Tabelle betreffen, wie TRUNCATE und REFERENCES, nicht durch Row Security Policies abgedeckt. Für B2B-Plattformen mit automatisierten Beschaffungsprozessen oder Bot-Detection-Szenarien, bei denen Bulk-Operationen zum Einsatz kommen, muss die Absicherung dieser Befehle separat über das klassische SQL-Rechtesystem erfolgen.


Fazit für Entscheider

RLS in PostgreSQL verlagert die Durchsetzung von Mandantengrenzen von der Anwendungsschicht in die Datenbank. Das erhöht die Ausfallsicherheit der Datenisolierung erheblich. Die Performance-Auswirkungen bei großen Datensätzen sind durch gezieltes Indexdesign beherrschbar. Entscheider sollten die Konfiguration jedoch nicht als einmalige Aufgabe betrachten: Jede neue Tabelle, jede neue Rolle und jede Bulk-Operation erfordert eine explizite Prüfung.


Quellen

Häufige Fragen

Was ist PostgreSQL Row-Level Security (RLS) und warum ist sie für Multi-Tenant-E-Commerce relevant?

RLS ist eine PostgreSQL-Funktion, die über das Standard-SQL-Rechtesystem hinausgeht. Sie erlaubt es, pro Nutzer oder Rolle festzulegen, welche Tabellenzeilen lesbar, einfügbar, aktualisierbar oder löschbar sind. Für Multi-Tenant-Anwendungen bedeutet das: Die Datenisolierung wird auf Datenbankebene erzwungen, nicht nur im Anwendungscode. Selbst wenn ein Entwickler eine WHERE-Bedingung vergisst, verhindert die Datenbank den mandantenübergreifenden Datenzugriff automatisch.

Wie aktiviert man RLS auf einer bestehenden Tabelle und welche Standardregel gilt ohne explizite Policy?

RLS wird per ALTER TABLE … ENABLE ROW LEVEL SECURITY aktiviert. Wichtig: Ist RLS aktiv, aber keine Policy definiert, greift automatisch eine Default-Deny-Regel. Das bedeutet, keine Zeile ist lesbar oder veränderbar. Der Tabelleneigentümer ist davon typischerweise ausgenommen. Operationen wie TRUNCATE oder REFERENCES unterliegen grundsätzlich nicht der RLS. Policies lassen sich auf einzelne Befehle (SELECT, INSERT, UPDATE, DELETE) oder auf bestimmte Rollen beschränken.

Wie unterscheidet sich datenbankbasierte Isolierung via RLS von der klassischen Filterung im Anwendungscode?

Beim klassischen Ansatz filtert der Anwendungscode: SELECT * FROM invoices WHERE tenant_id = $1. Fehlt diese Bedingung durch einen Bug, sieht ein Mandant fremde Daten. Mit RLS fügt PostgreSQL die Policy-Bedingung automatisch an jede Abfrage an, etwa WHERE tenant_id = current_tenant_id(). Selbst fehlerhafte Anwendungslogik kann den Schutz dann nicht umgehen. Laut oneuptime.com kann ein einziger solcher Bug das Vertrauen von Kunden dauerhaft zerstören.

Welche konkreten Performance-Aspekte müssen bei RLS mit Millionen von Zeilen berücksichtigt werden?

RLS-Policies werden als zusätzliche Filterbedingung in jede Abfrage eingebettet. Bei großen Tabellen ist daher ein Index auf der Isolierungsspalte, etwa org_id oder tenant_id, entscheidend. Ohne passenden Index führt PostgreSQL bei jeder Abfrage einen Sequentialscan durch, was bei Millionen Zeilen kritisch ist. Die Policy-Bedingung sollte zudem möglichst einfach gehalten werden, da komplexe Ausdrücke den Query-Planner belasten. Crunchy Data empfiehlt, die Tenant-Spalte als UUID zu definieren und konsequent zu indizieren.

Kann eine RLS-Policy auf bestimmte SQL-Befehle begrenzt werden, oder gilt sie immer für alle Operationen?

Policies sind feingranular steuerbar. Sie können für ALL, SELECT, INSERT, UPDATE oder DELETE separat definiert werden. Mehrere Policies auf einer Tabelle werden kombiniert ausgewertet. Das ermöglicht z.B. eine strenge Lesepolicy für alle Rollen, aber eine großzügigere Schreibpolicy für Service-Accounts. Laut PostgreSQL-Dokumentation lassen sich Policies sowohl auf Befehle als auch auf Rollen beziehen, was für komplexe Berechtigungsmodelle im E-Commerce-Umfeld flexibel einsetzbar ist.


Dieser Artikel wurde von einem KI-System automatisiert erstellt. Kennzeichnung gemäß Art. 50 der EU-KI-Verordnung.