Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Einführung in die Sicherheit auf Zeilenebene in SQL Server

Warum ist die Sicherheit auf Zeilenebene wichtig?

Vor SQL Server 2016 war die Sicherheit auf Tabellenebene die standardmäßig niedrigste Sicherheitsebene für eine Datenbank. Mit anderen Worten, ein Benutzer könnte eingeschränkt werden, um auf eine Tabelle als Ganzes zuzugreifen. In einigen Fällen müssen Benutzer jedoch Zugriff auf eine Tabelle haben, aber nicht auf bestimmte Zeilen innerhalb der Tabelle. Vor SQL Server 2016 mussten hierfür benutzerdefinierte gespeicherte Prozeduren geschrieben werden, um eine solche feinkörnige Sicherheit bereitzustellen. Solche gespeicherten Prozeduren sind jedoch anfällig für SQL-Injection und andere Sicherheitsvorkehrungen.

Verwenden der Sicherheitsfunktion auf Zeilenebene von SQL Server in der Praxis

SQL Server 2016 hat eine neue Sicherheitsfunktion auf Zeilenebene eingeführt, die Benutzern den Zugriff auf eine Tabelle ermöglicht, sie jedoch auf den Zugriff auf bestimmte Zeilen innerhalb dieser Tabelle beschränkt. Schauen wir uns an, wie dies praktisch genutzt werden kann.

Beschreibung

Es gibt vier Schritte zum Implementieren der Sicherheit auf Zeilenebene in SQL Server.

  1. Erteilen Sie den Benutzern für die Tabelle, für die Sie Sicherheit auf Zeilenebene implementieren möchten, Select-Berechtigungen.
  2. Als nächstes müssen Sie eine Inline-Tabellenwertfunktion schreiben, die ein Filterprädikat enthält. Fügen Sie die Filterlogik zum Filterprädikat hinzu.
  3. Schließlich müssen Sie das Filterprädikat, das Sie im zweiten Schritt erstellt haben, an eine Sicherheitsrichtlinie binden.
  4. Testen Sie die Sicherheitsfunktion auf Zeilenebene.

Bevor wir die obigen Schritte ausführen, müssen wir eine Dummy-Datenbank mit einigen Dummy-Datensätzen erstellen. Führen Sie dazu das folgende Skript aus:

CREATE DATABASE University
GO

USE University
GO

USE University
CREATE TABLE Persons
(
Id INT PRIMARY KEY IDENTITY(1,1),
Name VARCHAR (50),
Role VARCHAR (50)
)
GO

USE University
INSERT INTO Persons VALUES ('Sally', 'Principal' )
INSERT INTO Persons VALUES ('Edward', 'Student' )
INSERT INTO Persons VALUES ('Jon', 'Student' )
INSERT INTO Persons VALUES ('Scot', 'Student')
INSERT INTO Persons VALUES ('Ben', 'Student' )
INSERT INTO Persons VALUES ('Isabel', 'Teacher' )
INSERT INTO Persons VALUES ('David', 'Teacher' )
INSERT INTO Persons VALUES ('Laura', 'Teacher' )
INSERT INTO Persons VALUES ('Jean', 'Teacher')
INSERT INTO Persons VALUES ('Francis', 'Teacher' )

Im Skript erstellen wir eine Dummy-Datenbank „Universität“. Als nächstes führen wir das Skript aus, das eine Tabelle mit dem Namen „Persons“ erstellt. Wenn Sie sich das Tabellendesign ansehen, sehen Sie, dass es drei Spalten Id, Name und Role enthält. Die ID-Spalte ist die Primärschlüsselspalte mit IDENTITY-Einschränkung. Die Spalte Name enthält den Namen der Person und die Spalte Rolle enthält die Rolle der Person. Schließlich haben wir 10 Datensätze in die Personentabelle eingefügt. Der Tisch hat 1 Schulleiter, 4 Lehrer und 5 Schüler.

Lassen Sie uns eine einfache SELECT-Anweisung ausführen, um Datensätze in der Tabelle anzuzeigen:

Use University
SELECT * FROM Persons

Das Ergebnis sieht so aus:

Wir möchten, dass der Benutzer mit dem Namen „Principal“ Zugriff auf alle Zeilen in der Tabelle „Persons“ hat. Ebenso sollte ein Lehrer nur Zugriff auf die Lehrerdatensätze haben, während Schüler nur Zugriff auf die Schülerdatensätze haben sollten. Dies ist ein klassischer Fall von Sicherheit auf Zeilenebene.

Um die Sicherheit auf Zeilenebene zu implementieren, folgen wir den zuvor besprochenen Schritten.

Schritt 1:Benutzern ausgewählte Berechtigungen für die Tabelle erteilen

Lassen Sie uns drei Benutzer mit den Rollen „Principal“, „Lehrer“ und „Student“ erstellen und ihnen SELECT-Zugriff auf diese Benutzer in der Tabelle „Persons“ gewähren. Führen Sie dazu das folgende Skript aus:

CREATE USER Principal WITHOUT LOGIN;
GO
CREATE USER Teacher WITHOUT LOGIN;
GO
CREATE USER Student WITHOUT LOGIN;
GO

Use University
GRANT SELECT ON Persons TO Principal;
GO
GRANT SELECT ON Persons TO Teacher;
GO
GRANT SELECT ON Persons TO Student;
GO

Schritt 2:Filterprädikat erstellen

Nachdem den Benutzern Berechtigungen erteilt wurden, besteht der nächste Schritt darin, ein Filterprädikat zu erstellen.

Das folgende Skript macht das:

Use University
GO
CREATE FUNCTION dbo.fn_SP_Person(@Role AS sysname)
    RETURNS TABLE
WITH SCHEMABINDING
AS
    RETURN SELECT 1 AS fn_SP_Person_output
    -- Predicate logic
    WHERE @Role = USER_NAME()
    OR USER_NAME() = 'Principal';
GO

Das Filterprädikat wird innerhalb einer Inline-Tabellenwertfunktion erstellt und übernimmt die Rolle des Benutzers als Parameter. Es gibt die Datensätze zurück, bei denen der als Parameter übergebene Rollenwert mit dem Rollenwert in der Rolle-Spalte übereinstimmt. Oder wenn die Benutzerrolle „Principal“ ist, werden alle Rollen zurückgegeben. Wenn Sie sich den Prädikatfilter ansehen, werden Sie den Tabellennamen nicht finden, für den wir den Filter erstellen. Das Filterprädikat ist über die Sicherheitsrichtlinie, die wir im nächsten Schritt sehen werden, mit der Tabelle verbunden.

Schritt 3:Erstellen einer Sicherheitsrichtlinie

Führen Sie das folgende Skript aus, um eine Sicherheitsrichtlinie für das Filterprädikat zu erstellen, das wir im letzten Schritt erstellt haben:

Use University
Go
CREATE SECURITY POLICY RoleFilter
ADD FILTER PREDICATE dbo.fn_SP_Person(Role)
ON dbo.Persons
WITH (STATE = ON);
GO

In der Sicherheitsrichtlinie haben wir einfach das von uns erstellte Filterprädikat zur Tabelle „Personen“ hinzugefügt. Um die Richtlinie zu aktivieren, sollte das „STATE“-Flag auf ON gesetzt werden.

Schritt 4:Sicherheit auf Zeilenebene testen

Wir haben alle erforderlichen Schritte durchgeführt, um die Sicherheit auf Zeilenebene in der Personentabelle der Universitätsdatenbank durchzusetzen. Versuchen wir zunächst, über den Standardbenutzer auf die Datensätze in der Personentabelle zuzugreifen. Führen Sie das folgende Skript aus:

Use University
SELECT * FROM Persons;
GO

In der Ausgabe sehen Sie nichts, da der Standardbenutzer nicht auf die Personentabelle zugreifen kann.

Wechseln wir zu dem zuvor erstellten Student-Benutzer und versuchen, Datensätze aus der Personentabelle auszuwählen:

EXECUTE AS USER = 'Student';
Use University
SELECT * FROM Persons; -- Student Records Only
REVERT;
GO

Im obigen Skript wechseln wir zum Benutzer „Student“, wählen Datensätze aus der Tabelle „Personen“ aus und kehren zum Standardbenutzer zurück. Die Ausgabe sieht so aus:

Sie können sehen, dass aufgrund der Sicherheit auf Zeilenebene nur Datensätze angezeigt werden, bei denen die Spalte „Rolle“ den Wert „Student“ hat.

Ebenso hat der Benutzer „Lehrer“ nur Zugriff auf Datensätze, in denen die Spalte „Rolle“ den Wert „Lehrer“ hat. Führen Sie das folgende Skript aus, um dies zu überprüfen:

EXECUTE AS USER = 'Teacher';
Use University
SELECT * FROM Persons; -- All Records
REVERT;
GO

In der Ausgabe erhalten Sie die folgenden Datensätze:

Schließlich haben wir in unserem Filterprädikat die Logik implementiert, dass der Benutzer Principal auf alle Datensätze zugreifen kann. Lassen Sie uns dies überprüfen, indem Sie die folgende Abfrage ausführen:

EXECUTE AS USER = 'Principal';
Use University
SELECT * FROM Persons; -- All Records
REVERT;
GO

In der Ausgabe sehen Sie alle Datensätze wie unten gezeigt:

Schlussfolgerung

Die Sicherheitsfunktion auf Zeilenebene ist äußerst nützlich, wenn Sie möchten, dass Benutzer detaillierten Zugriff auf bestimmte Daten haben. Die Sicherheitsfunktion auf Zeilenebene beinhaltet jedoch eine Inline-Tabellenwertfunktion, die zu Leistungseinbußen führen kann.

Als Faustregel gilt:Wenn Sie planen, eine einfache WHERE-Klausel in der Prädikatfunktion zu verwenden, sollte Ihre Leistung nicht beeinträchtigt werden. Andererseits sollten komplexe Join-Anweisungen mit Nachschlagetabellen vermieden werden, wenn Sie Sicherheit auf Zeilenebene implementiert haben.