Inspiré par diverses questions liées au schéma que j'ai vues ...
Chaîne de propriété me permet d'accorder une exécution sur une procédure stockée sans autorisations explicites sur les tables que j'utilise, si la procédure stockée et les tables sont dans le même schéma.
Si nous utilisons des schémas distincts, je devrais explicitement octroyer XXX sur les tables de schémas différents. L'exemple de chaînage de propriété le démontre. Cela signifie que l'utilisateur exécutant le proc stocké peut lire/écrire vos tables directement.
Ce serait comme avoir un accès direct à vos variables d'instance dans une classe, contourner les getter/setters, rompre l'encapsulation.
Nous utilisons également la sécurité au niveau des lignes pour restreindre ce que quelqu'un voit et nous l'appliquons dans les procédures stockées.
Alors, comment pouvons-nous maintenir la séparation des schémas et empêcher l'accès direct aux tables?
Bien sûr, la question ne s'applique pas si vous utilisez un ORM ou n'utilisez pas de proc stockés. Mais je ne demande pas si je dois utiliser un ORM ou un proc stocké au cas où quelqu'un ressentirait le besoin de m'éclairer ...
Modifier, exemple
CREATE USER OwnsMultiSchema WITHOUT LOGIN
GO
CREATE SCHEMA MultiSchema1 AUTHORIZATION OwnsMultiSchema
GO
CREATE SCHEMA MultiSchema2 AUTHORIZATION OwnsMultiSchema
GO
CREATE USER OwnsOtherSchema WITHOUT LOGIN
GO
CREATE SCHEMA OtherSchema AUTHORIZATION OwnsOtherSchema
GO
CREATE TABLE MultiSchema1.T1 (foo int)
GO
CREATE TABLE MultiSchema2.T2 (foo int)
GO
CREATE TABLE OtherSchema.TA (foo int)
GO
CREATE PROC MultiSchema1.P1
AS
SELECT * FROM MultiSchema1.T1
SELECT * FROM MultiSchema2.T2
SELECT * FROM OtherSchema.TA
Go
EXEC AS USER = 'OwnsMultiSchema'
GO
--gives error on OtherSchema
EXEC MultiSchema1.P1
GO
REVERT
GO
CREATE PROC OtherSchema.PA
AS
SELECT * FROM MultiSchema1.T1
SELECT * FROM MultiSchema2.T2
SELECT * FROM OtherSchema.TA
Go
GRANT EXEC ON OtherSchema.PA TO OwnsMultiSchema
GO
EXEC AS USER = 'OwnsMultiSchema'
GO
--works
EXEC OtherSchema.PA
GO
REVERT
GO
Modifier 2:
Je crains que votre description ou votre conception du chaînage de propriété ne soit pas claire, alors permettez-moi de commencer par cela:
"Chaîne de propriété" se réfère simplement à ce fait que lors de l'exécution d'une procédure stockée (ou vue) sur SQL Server, le lot en cours d'exécution acquiert temporairement les droits/autorisations du propriétaire du sProc (ou du propriétaire du schéma du sProc) lors de l'exécution de ce code SQL. Ainsi, dans le cas d'un sProc, l'utilisateur ne peut pas utiliser ces privilèges pour faire quoi que ce soit que le code sProc n'implémente pas pour eux. Notez surtout qu'il n'acquiert jamais Identité du Propriétaire, seulement ses droits, temporairement (cependant, EXECUTE AS ... le fait).
Ainsi, l'approche typique pour tirer parti de cela pour la sécurité consiste à:
Placez toutes les tables de données (et toutes les vues non liées à la sécurité) dans leur propre schéma, appelons-le [données] (bien que généralement [dbo] soit utilisé car il est déjà là et trop privilégié pour le schéma de l'utilisateur). Assurez-vous qu'aucun utilisateur, schéma ou propriétaire existant n'a accès à ce schéma [de données].
Créez un schéma appelé [exec] pour tous les sProcs (et/ou éventuellement toutes les vues de sécurité). Assurez-vous que le propriétaire de ce schéma a accès au schéma [data] (c'est facile si vous faites de dbo le propriétaire de ce schéma).
Créez un nouveau rôle db appelé "Users" et donnez-lui un accès EXECUTE au schéma [exec]. Ajoutez maintenant tous les utilisateurs à ce rôle. Assurez-vous que vos utilisateurs ne disposent que des droits de connexion et n'ont aucun accès autorisé à tout autre schéma, y compris [dbo].
Vos utilisateurs peuvent désormais accéder aux données uniquement en exécutant les sProcs dans [exec]. Ils ne peuvent accéder à aucune autre donnée ni exécuter aucun autre objet.
Je ne sais pas si cela répond à votre question (car je ne savais pas exactement quelle était la question), alors n'hésitez pas à me rediriger.
En ce qui concerne la sécurité au niveau des lignes, voici comment je le fais toujours avec le schéma de sécurité ci-dessus:
J'implémente toujours la sécurité au niveau de la ligne sous la forme d'une série de vues qui enveloppent chaque table dans un miroir et comparent l'identité de l'utilisateur (généralement avec Suser_Sname () ou l'une des autres) à une liste de sécurité saisie à partir d'un code de sécurité dans la ligne elle-même. Ce sont les vues de sécurité.
Créez un nouveau schéma appelé [lignes], donnez à son propriétaire un accès au schéma [données] et rien d'autre. Mettez toutes les vues de sécurité dans ce schéma.
Révoquez l'accès du propriétaire [exec] au schéma [data] et accordez-lui à la place un accès aux données au schéma [rows].
Terminé. Maintenant, la sécurité au niveau des lignes a été implémentée en la glissant de manière transparente entre les sProcs et les tables.
Enfin, voici un produit stocké que j'utilise pour m'aider à me souvenir de la quantité de ce truc de sécurité obscur qui fonctionne et interagit avec lui-même ( oups, version corrigée du code ):
CREATE proc [TestCnxOnly].[spShowProc_Security_NoEX] as
--no "With Execute as Owner" for this version
--create User [UserNoLogin] without login
--Grant connect on database :: TestSecurity to Guest
--alter database TestSecurity set trustworthy on
--Show current user context:
select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (sproc)]
, suser_sname() as sname
, system_user as system_
--Execute As Login = 'UserNoLogin'
select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (after exec as)]
, suser_sname() as sname
, system_user as system_
EXEC('select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (in Exec(sql))]
, suser_sname() as sname
, system_user as system_')
EXEC sp_ExecuteSQL N'select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (in sp_Executesql)]
, suser_sname() as sname
, system_user as system_'
--Revert
select current_user as current_
, session_user as session
, user_name() as _name
, suser_name() as [suser (aftr revert)]
, suser_sname() as sname
, system_user as system_
[EDIT: version corrigée du code)
Mon 2c: le chaînage de propriété est un héritage. Il date de jours où il n'y avait pas d'alternatives, et par rapport aux alternatives d'aujourd'hui, il n'est pas sûr et grossier.
Je dis que l'alternative n'est pas les autorisations de schéma, l'alternative est la signature de code. Avec la signature de code, vous pouvez accorder les autorisations nécessaires sur la signature de la procédure et accorder un accès à l'exécution large sur la procédure pendant que l'accès aux données est étroitement contrôlé. La signature de code offre un contrôle plus granulaire et plus précis, et elle ne peut pas être utilisée de la même manière que le chaînage de propriété. Il fonctionne à l'intérieur du schéma, il fonctionne à travers le schéma, il fonctionne à travers la base de données et ne nécessite pas l'énorme trou de sécurité du chaînage de la propriété croisée de la base de données pour être ouvert. Et il ne nécessite pas le détournement de la propriété de l'objet à des fins d'accès: le propriétaire de la procédure peut être n'importe quel utilisateur.
Quant à votre deuxième question sur la sécurité au niveau des lignes: la sécurité au niveau des lignes n'existe pas vraiment dans les versions de SQL Server 2014 et antérieures, en tant que fonctionnalité offerte par le moteur. Vous disposez de plusieurs solutions de contournement, et ces solutions de contournement fonctionnent en fait mieux avec la signature de code qu'avec le chaînage de propriété. Étant donné que sys.login_token contient les signatures de contexte et les contresignatures, vous pouvez réellement effectuer des vérifications plus complexes que dans un contexte de chaîne de propriété.
Depuis la version 2016, SQL Server prend entièrement en charge sécurité au niveau des lignes .
Vous pouvez:
Grant Execute On Schema::[schema_name] To [user_name]
pour permettre à l'utilisateur d'exécuter toutes les procédures du schéma. Si vous ne voulez pas qu'il soit en mesure de les exécuter tous, vous pouvez explicitement refuser à l'utilisateur d'exécuter une procédure particulière. Le refus aura priorité dans ce cas.