Row Level Security ist die wirksamste einzelne Massnahme gegen den Fehler, der ein SaaS-Produkt am schnellsten erledigt: Daten eines Kunden bei einem anderen. Sie verschiebt die Durchsetzung der Mandantengrenze von der Anwendung in die Datenbank — dorthin, wo sie nicht vergessen werden kann.
Warum Filter in der Anwendung nicht genügen
Die übliche Lösung ist eine Bedingung in jeder Abfrage. Sie funktioniert bei jeder einzelnen Abfrage, die sie enthält, und versagt vollständig bei der einen, die sie nicht enthält.
Diese eine Abfrage entsteht zuverlässig: in einem Bericht, in einem Wartungsskript, in einer neuen Funktion unter Zeitdruck, in einer Migration. Keiner dieser Fälle ist Nachlässigkeit — sie sind die statistische Folge davon, dass eine Sicherheitseigenschaft von menschlicher Disziplin abhängt.
Mit Row Level Security kehrt sich das Verhalten um: Eine Abfrage ohne gesetzten Mandantenkontext liefert keine Zeilen. Der Fehlerfall ist damit ein leerer Bildschirm statt eines Datenabflusses.
Wie der Kontext an die Datenbank kommt
Die Regeln brauchen eine Information darüber, wer fragt. Zwei Wege sind üblich: Der Kontext kommt aus einem verifizierten Token, das die Datenbank selbst auswertet, oder er wird pro Verbindung als Sitzungsvariable gesetzt.
Der zweite Weg ist der Punkt, an dem es gefährlich wird. Wird der Kontext pro Verbindung gesetzt und die Verbindung anschliessend an einen anderen Vorgang weitergereicht, kann der Kontext des vorherigen bestehen bleiben. In Kombination mit Verbindungsbündelung ist das ein realer Fehlerpfad.
Die Regel lautet deshalb: Kontext innerhalb einer Transaktion setzen, sodass er mit ihr endet — nicht für die Dauer der Verbindung.
Der Dienstschlüssel umgeht alles
Jede Plattform kennt einen Zugang, der die Regeln umgeht, damit Verwaltungsaufgaben möglich bleiben. Dieser Zugang ist der wichtigste einzelne Gegenstand im gesamten System.
Er gehört ausschliesslich in serverseitigen Code, nie in etwas, das im Browser landet, und sein Einsatz sollte auf wenige, klar benannte Stellen begrenzt sein. Ein Produkt, das ihn für den normalen Betrieb verwendet, hat die Schutzebene, die es aufgebaut hat, wieder abgeschaltet.
Was die Regeln mit der Geschwindigkeit machen
Eine Regel ist eine zusätzliche Bedingung in jeder Abfrage. Damit sie nicht bremst, muss die Spalte, auf die sie sich stützt, indiziert sein — und bei zusammengesetzten Indizes gehört sie an die erste Stelle.
Der zweite Punkt betrifft die Komplexität der Regeln selbst. Eine Regel, die eine Unterabfrage über mehrere Tabellen ausführt, wird für jede Zeile ausgewertet. Solche Regeln werden schnell zum Engpass. Die tragfähigere Variante ist, die nötige Information zu denormalisieren, sodass die Regel eine einfache Bedingung bleibt.
Funktionen über Mandantengrenzen hinweg
Irgendwann kommt eine Anforderung, die die Grenze überschreiten soll: eine Vergleichsansicht, eine geteilte Ressource, eine Auswertung über alle Kunden. Die Versuchung ist, dafür den Dienstschlüssel zu verwenden.
Sauberer ist, die Ausnahme in die Regel aufzunehmen — etwa über eine Freigabetabelle, die explizit macht, welcher Mandant auf welche fremde Ressource zugreifen darf. Die Ausnahme wird damit Teil des überprüfbaren Modells statt eine Umgehung davon.
Testen ist der Teil, der entscheidet
Alles oben ist wirkungslos ohne einen Test, der bei jedem Release läuft: zwei Mandanten anlegen, mit dem Kontext des einen auf die Daten des anderen zugreifen, und erwarten, dass nichts zurückkommt.
Dieser eine Test ist mehr wert als eine ausführliche Dokumentation der Regeln. Er ist auch der einzige Weg zu bemerken, dass eine neue Tabelle ohne aktivierte Regeln angelegt wurde — der häufigste Fehler nach der Ersteinrichtung.

