Implementieren von Sicherheit auf Zeilenebene

Abgeschlossen

Die Sicherheit auf Zeilenebene (Row-Level Security, RLS) verwendet keine Verschlüsselung und wird auf Datenbankebene angewendet, um den Zugriff auf eine Tabelle mithilfe einer Sicherheitsrichtlinie zu beschränken, die auf einer Gruppenmitgliedschaft oder einem Autorisierungskontext basiert. 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.

    Access 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 zutreffend
  • Blockprädikate: Sie schränken Datenänderungen ein, die gegen das Prädikat verstoßen.

    Access Definition
    AFTER INSERT Verhindert, dass Benutzer Zeilen mit Werten einfügen, die gegen das Prädikat verstoßen.
    AFTER UPDATE Verhindert, dass Benutzer Zeilen mit Werten aktualisieren, die gegen das Prädikat verstoßen.
    BEFORE UPDATE Verhindert, dass Benutzer Zeilen aktualisieren, die derzeit gegen das Prädikat verstoßen.
    BEFORE DELETE 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:

  1. Erstellen Sie die Benutzer oder Gruppen, für die ein separater Zugriff eingerichtet werden soll.
  2. Erstellen Sie die Inline-Tabellenwertfunktion, die die Ergebnisse auf der Grundlage des definierten Prädikats filtert.
  3. Erstellen Sie eine Sicherheitsrichtlinie für die Tabelle, und weisen Sie ihr die oben erstellte Funktion zu.

Die folgenden T-SQL-Befehle zeigen, wie Sie RLS in einem Szenario verwenden, in dem der Benutzerzugriff nach Mandanten getrennt ist:

-- 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 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 Tenant1, Tenant2, Tenant3 und Tenant4 sollten nur ihre eigenen Zeilen sehen.

Wenn Sie die Sicherheitsrichtlinie mit WITH (STATE = OFF); ändern, werden Sie feststellen, dass die Benutzer alle Zeilen anzeigen können.

Screenshot of T-SQL commands to alter a security policy.

Hinweis

Es besteht ein Risiko von Informationslecks, wenn ein Angreifer eine Abfrage mit einer speziell gestalteten WHERE Klausel und z. B. einem Fehler vom Typ „Division durch Null“ schreibt, um eine Ausnahme zu erzwingen, wenn die WHERE-Bedingung TRUE lautet. Dies wird als Seitenkanalangriff bezeichnet. Es empfiehlt sich, die Möglichkeiten der Benutzer zum Ausführen von Ad-hoc-Abfragen einzuschränken, wenn Sie die Sicherheit auf Zeilenebene verwenden.

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 Methode

Im Folgenden finden Sie einige bewährte Methoden, die Sie bei der Implementierung von RLS berücksichtigen sollten:

  • Es wird empfohlen, ein separates Schema für Prädikatfunktionen und Sicherheitsrichtlinien zu erstellen.
  • Vermeiden Sie nach Möglichkeit Typenkonvertierungen in Prädikatfunktionen.
  • Vermeiden Sie übermäßige Tabellenverknüpfungen und Rekursionen in Prädikatfunktionen, um die Leistung zu maximieren.