Implementieren von Sicherheit auf Zeilenebene
Die Sicherheit auf Zeilenebene (Row-Level Security, RLS) verwendet keine Verschlüsselung und wird auf Datenbankebene verwendet, um den Zugriff auf eine Tabelle mithilfe einer Sicherheitsrichtlinie basierend auf der Gruppenmitgliedschaft oder dem Autorisierungskontext einzuschränken. Diese Funktionalität entspricht einer WHERE-Klausel.
Die Sicherheitsrichtlinie ruft eine Inline-Tabellenwertfunktion auf, um den Zugriff auf die Zeilen einer Tabelle zu schützen.
Abhängig vom Attribut eines Benutzers bestimmt das Prädikat, ob dieser Benutzer Zugriff auf die betreffenden Informationen hat. Wenn Sie eine Abfrage für eine Tabelle ausführen, wendet die Sicherheitsrichtlinie die Prädikatfunktion an. In Abhängigkeit von den geschäftlichen Anforderungen kann RLS so einfach wie WHERE CustomerId = 29 oder so komplex wie erforderlich sein.
Es gibt zwei Arten von Sicherheitsrichtlinien, die von der Sicherheit auf Zeilenebene unterstützt werden:
Filterprädikate: Sie beschränken den Zugriff auf Daten, die gegen das Prädikat verstoßen.
Zugriff Definition SELECT Gefilterte Zeilen können nicht angezeigt werden. UPDATE Gefilterte Zeilen können nicht aktualisiert werden. DELETE Gefilterte Zeilen können nicht gelöscht werden. INSERT Nicht anwendbar Blockprädikate: Sie schränken Datenänderungen ein, die gegen das Prädikat verstoßen.
Zugriff Definition NACH EINFÜGEN Verhindert, dass Benutzer Zeilen mit Werten einfügen, die gegen das Prädikat verstoßen. NACH AKTUALISIERUNG Verhindert, dass Benutzer Zeilen mit Werten aktualisieren, die gegen das Prädikat verstoßen. VOR AKTUALISIERUNG Verhindert, dass Benutzer Zeilen aktualisieren, die derzeit gegen das Prädikat verstoßen. VOR LÖSCHVORGANG Blockiert Löschvorgänge, wenn die Zeile gegen das Prädikat verstößt.
Da die Zugriffssteuerung auf Datenbankebene konfiguriert und angewendet wird, ergeben sich keine oder nur minimale Anwendungsänderungen. Außerdem haben die Benutzer direkten Zugriff auf die Tabellen und können ihre eigenen Daten abfragen.
Die Sicherheit auf Zeilenebene wird in drei Hauptschritten implementiert:
- Erstellen Sie die Benutzer oder Gruppen, für die ein separater Zugriff eingerichtet werden soll.
- Erstellen Sie die Inline-Tabellenwertfunktion, die die Ergebnisse auf Grundlage des definierten Prädikats filtert.
- Erstellen Sie eine Sicherheitsrichtlinie für die Tabelle, und weisen Sie die zuvor erstellte Funktion zu.
Die folgenden T-SQL-Befehle veranschaulichen die Verwendung von RLS in einem Szenario, in dem der Benutzerzugriff durch den Mandanten getrennt wird:
-- Create supporting objects for this example
CREATE TABLE [Sales] (SalesID INT,
ProductID INT,
TenantName NVARCHAR(10),
OrderQtd INT,
UnitPrice MONEY)
GO
INSERT INTO [Sales] VALUES (1, 3, 'Tenant1', 5, 10.00);
INSERT INTO [Sales] VALUES (2, 4, 'Tenant1', 2, 57.00);
INSERT INTO [Sales] VALUES (3, 7, 'Tenant1', 4, 23.00);
INSERT INTO [Sales] VALUES (4, 2, 'Tenant2', 2, 91.00);
INSERT INTO [Sales] VALUES (5, 9, 'Tenant3', 5, 80.00);
INSERT INTO [Sales] VALUES (6, 1, 'Tenant3', 5, 35.00);
INSERT INTO [Sales] VALUES (7, 3, 'Tenant4', 8, 11.00);
-- View all the rows in the table
SELECT * FROM Sales;
Als Nächstes erstellen Sie die Benutzer und gewähren ihnen Zugriff auf die Tabelle Sales. In diesem Beispiel ist jeder Benutzer für einen bestimmten Mandanten zuständig. Der Benutzer TenantAdmin hat Zugriff auf die Daten aller Mandanten.
CREATE USER [TenantAdmin] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant1] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant2] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant3] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant4] WITH PASSWORD = '<strong password>'
GO
GRANT SELECT ON [Sales] TO [TenantAdmin]
GO
GRANT SELECT ON [Sales] TO [Tenant1]
GO
GRANT SELECT ON [Sales] TO [Tenant2]
GO
GRANT SELECT ON [Sales] TO [Tenant3]
GO
GRANT SELECT ON [Sales] TO [Tenant4]
GO
Als Nächstes erstellen wir ein neues Schema und eine Inline-Tabellenwertfunktion und gewähren der Benutzerin bzw. dem Benutzer Zugriff auf die neue Funktion. Das Prädikat WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin' wird ausgewertet, wenn der Name des Benutzers, der die Abfrage ausführt, mit den Werten der Spalte TenantName übereinstimmt.
CREATE SCHEMA sec;
GO
--Create the filter predicate
CREATE FUNCTION sec.tvf_SecurityPredicatebyTenant(@TenantName AS NVARCHAR(10))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN SELECT 1 AS result
WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin';
GO
--Grant users access to inline table-valued function
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [TenantAdmin]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant1]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant2]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant3]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant4]
GO
--Create security policy and add the filter predicate
CREATE SECURITY POLICY sec.SalesPolicy
ADD FILTER PREDICATE sec.tvf_SecurityPredicatebyTenant(TenantName) ON [dbo].[Sales]
WITH (STATE = ON);
GO
An diesem Punkt sind wir bereit, den Zugriff zu testen:
EXECUTE AS USER = 'TenantAdmin';
SELECT * FROM dbo.Sales;
REVERT;
EXECUTE AS USER = 'Tenant1';
SELECT * FROM dbo.Sales;
REVERT;
EXECUTE AS USER = 'Tenant2';
SELECT * FROM dbo.Sales;
REVERT;
EXECUTE AS USER = 'Tenant3';
SELECT * FROM dbo.Sales;
REVERT;
EXECUTE AS USER = 'Tenant4';
SELECT * FROM dbo.Sales;
REVERT;
Der Benutzer TenantAdmin sollte alle Zeilen sehen. Die Benutzer "Mandant1", "Mandant2", " Mandant3" und " Mandant4 " sollten nur ihre eigenen Zeilen sehen.
Wenn Sie die Sicherheitsrichtlinie mit WITH (STATE = OFF); ändern, werden Sie feststellen, dass die Benutzerinnen und Benutzer alle Zeilen anzeigen können.
Hinweis
Es besteht ein Risiko für Informationslecks, wenn ein Angreifer eine Abfrage mit einer speziell gestalteten WHERE Klausel schreibt und z. B. einen Divid-by-Zero-Fehler, um eine Ausnahme zu erzwingen, wenn die WHERE Bedingung wahr ist. Dies wird als Seitenkanalangriff bezeichnet. Es ist ratsam, die Fähigkeit von Benutzern zu beschränken, ungeplante Abfragen auszuführen, wenn die Sicherheit auf Zeilenebene verwendet wird.
Anwendungsfall
Die Sicherheit auf Zeilenebene eignet sich ideal für viele Szenarien, darunter z. B. folgende:
- Sie müssen den Abteilungszugriff auf Zeilenebene einschränken.
- Sie müssen den Kundenzugriff auf die Daten beschränken, die für ihr Unternehmen relevant sind.
- Sie müssen den Zugriff aus Compliancegründen einschränken.
Bewährte Methoden
Im Folgenden finden Sie einige bewährte Methoden, die Sie bei der Implementierung von RLS berücksichtigen sollten:
- Erstellen Sie ein separates Schema für Prädikatfunktionen und Sicherheitsrichtlinien.
- Vermeiden Sie Typkonvertierungen in Prädikatfunktionen.
- Vermeiden Sie übermäßige Tabellenverknnungen und Rekursionen in Prädikatfunktionen.