Pourquoi la sécurité au niveau des lignes est-elle importante ?
Avant SQL Server 2016, la sécurité au niveau de la table était le niveau de sécurité le plus bas par défaut pour une base de données. En d'autres termes, un utilisateur pourrait être limité à l'accès à une table dans son ensemble. Cependant, dans certains cas, nous avons besoin que les utilisateurs aient accès à une table, mais pas à des lignes spécifiques de la table. Avant SQL Server 2016, cela nécessitait l'écriture de procédures stockées personnalisées pour fournir une sécurité aussi fine. Cependant, ces procédures stockées sont sujettes à l'injection SQL et à d'autres mises en garde de sécurité.
Utilisation de la fonctionnalité de sécurité au niveau des lignes de SQL Server lors de la pratique
SQL Server 2016 a introduit une nouvelle fonctionnalité de sécurité au niveau des lignes qui permet aux utilisateurs d'accéder à une table mais les limite à l'accès à des lignes spécifiques de cette table. Voyons comment cela peut être utilisé concrètement.
Description
Il existe quatre étapes pour implémenter la sécurité au niveau des lignes dans SQL Server.
- Accordez des autorisations de sélection aux utilisateurs de la table sur laquelle vous souhaitez mettre en œuvre la sécurité au niveau des lignes.
- Ensuite, vous devez écrire une fonction de valeur de table en ligne contenant un prédicat de filtre. Ajoutez la logique de filtre au prédicat de filtre.
- Enfin, vous devez lier le prédicat de filtre que vous avez créé à la deuxième étape à une stratégie de sécurité.
- Testez la fonctionnalité de sécurité au niveau des lignes.
Avant d'effectuer les étapes ci-dessus, nous devons créer une base de données factice avec des enregistrements factices. Exécutez le script suivant pour ce faire :
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' )
Dans le script, nous créons une base de données factice "University". Ensuite, nous exécutons le script qui crée une table nommée "Persons". Si vous regardez la conception de la table, vous pouvez voir qu'elle contient trois colonnes Id, Name et Role. La colonne Id est la colonne de clé primaire avec la contrainte IDENTITY. La colonne Nom contient le nom de la personne et la colonne Rôle contient le rôle de la personne. Enfin, nous avons inséré 10 enregistrements dans la table Persons. La table compte 1 directeur, 4 enseignants et 5 élèves.
Exécutons une simple instruction SELECT pour voir les enregistrements dans la table :
Use University SELECT * FROM Persons
Le résultat ressemble à ceci :
Nous voulons que l'utilisateur nommé Principal ait accès à toutes les lignes de la table Persons. De même, un enseignant ne doit avoir accès qu'aux enregistrements de l'enseignant, tandis que les étudiants ne doivent avoir accès qu'aux enregistrements des étudiants. Il s'agit d'un cas classique de sécurité au niveau des lignes.
Pour implémenter la sécurité au niveau des lignes, nous suivrons les étapes décrites précédemment.
Étape 1 :Accorder des autorisations de sélection aux utilisateurs de la table
Créons trois utilisateurs avec les rôles Principal, Enseignant et Étudiant et accordons-leur l'accès SELECT à ces utilisateurs sur la table Personnes. Exécutez le script suivant pour ce faire :
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
Étape 2 :Créer un prédicat de filtre
Une fois les autorisations accordées aux utilisateurs, l'étape suivante consiste à créer un prédicat de filtre.
Le script suivant fait cela :
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
Le prédicat de filtre est créé à l'intérieur d'une fonction table en ligne et prend le rôle de l'utilisateur en tant que paramètre. Il renvoie les enregistrements où la valeur Rôle transmise en tant que paramètre correspond à la valeur de rôle dans la colonne Rôle. Ou si le rôle de l'utilisateur est "Principal", tous les rôles sont renvoyés. Si vous regardez le filtre de prédicat, vous ne trouverez pas le nom de la table pour laquelle nous créons le filtre. Le prédicat du filtre est connecté à la table via la politique de sécurité que nous verrons à l'étape suivante.
Étape 3 :Créer une politique de sécurité
Exécutez le script suivant pour créer une politique de sécurité pour le prédicat de filtre que nous avons créé à la dernière étape :
Use University Go CREATE SECURITY POLICY RoleFilter ADD FILTER PREDICATE dbo.fn_SP_Person(Role) ON dbo.Persons WITH (STATE = ON); GO
Dans la politique de sécurité, nous avons simplement ajouté le prédicat de filtre que nous avons créé à la table Persons. Pour activer la stratégie, l'indicateur "STATE" doit être défini sur ON.
Étape 4 :Tester la sécurité au niveau de la ligne
Nous avons effectué toutes les étapes nécessaires pour appliquer la sécurité au niveau des lignes sur la table Persons de la base de données University. Essayons d'abord d'accéder aux enregistrements de la table Persons via l'utilisateur par défaut. Exécutez le script suivant :
Use University SELECT * FROM Persons; GO
Vous ne verrez rien dans la sortie car l'utilisateur par défaut ne peut pas accéder à la table Persons.
Passons à l'utilisateur Étudiant que nous avons créé précédemment et essayons de SÉLECTIONNER les enregistrements de la table Personnes :
EXECUTE AS USER = 'Student'; Use University SELECT * FROM Persons; -- Student Records Only REVERT; GO
Dans le script ci-dessus, nous passons à l'utilisateur "Étudiant", sélectionnons les enregistrements de la table Personnes et revenons à l'utilisateur par défaut. La sortie ressemble à ceci :
Vous pouvez voir qu'en raison de la sécurité au niveau de la ligne, seuls les enregistrements dont la colonne Rôle a la valeur Étudiant sont affichés.
De même, l'utilisateur Enseignant n'aura accès qu'aux enregistrements où la colonne Rôle a la valeur Enseignant. Exécutez le script suivant pour le vérifier :
EXECUTE AS USER = 'Teacher'; Use University SELECT * FROM Persons; -- All Records REVERT; GO
Dans la sortie, vous aurez les enregistrements suivants :
Enfin, dans notre prédicat de filtre, nous avons implémenté la logique selon laquelle l'utilisateur Principal peut accéder à tous les enregistrements. Vérifions cela en exécutant la requête suivante :
EXECUTE AS USER = 'Principal'; Use University SELECT * FROM Persons; -- All Records REVERT; GO
Dans la sortie, vous verrez tous les enregistrements comme indiqué ci-dessous :
Conclusion
La fonctionnalité de sécurité au niveau des lignes est extrêmement utile lorsque vous souhaitez que les utilisateurs aient un accès précis à des données spécifiques. Cependant, la fonctionnalité de sécurité au niveau des lignes implique une fonction de table en ligne, ce qui peut entraîner une baisse des performances.
En règle générale, si vous envisagez d'utiliser une simple clause WHERE dans la fonction de prédicat, vos performances ne devraient pas être affectées. D'autre part, les instructions de jointure complexes impliquant des tables de recherche doivent être évitées lorsque vous avez implémenté la sécurité au niveau des lignes.