web-dev-qa-db-fra.com

Vue d'accès basée sur la table dans une autre base de données sans compte dans cette autre base de données

J'ai créé une vue dans database1 basée sur des tables dans database2. J'ai donné l'autorisation SELECT à un utilisateur qui n'a accès qu'à database1. L'utilisateur ne peut pas faire fonctionner cette vue car il n'a pas de compte dans database2. Comment puis-je résoudre ce problème? Je ne veux pas créer de compte dans database2.

10
tom

Ceci est facile à réaliser de manière très sécurisée en utilisant la signature de module. Ce sera similaire aux deux réponses suivantes, également ici sur DBA.StackExchange, qui donnent des exemples de ce fait:

Sécurité des procédures stockées avec exécution en tant que, requêtes de bases de données croisées et signature de module

Autorisations dans les déclencheurs lors de l'utilisation de certificats croisés

La différence pour cette question particulière est qu'elle traite d'une vue et que les vues ne peuvent pas être signées. Ainsi, vous devrez changer la vue en une fonction table à valeurs multiples (TVF) car celles-ci peuvent être signées et accessibles comme une vue (enfin, pour l'accès SELECT).

L'exemple de code suivant montre faire exactement ce qui est demandé dans la question en ce que le login/utilisateur "RestrictedUser" n'a accès qu'à "DatabaseA" et peut encore obtenir des données de "DatabaseB". Cela fonctionne uniquement en sélectionnant celui-ci TVF, et uniquement en raison de sa signature.

Accomplir ce type d'accès entre bases de données tout en utilisant une vue, et pas donnant à l'utilisateur des autorisations supplémentaires, nécessiterait d'activer le chaînage de propriété entre bases de données. C'est beaucoup moins sûr car il est complètement ouvert pour tous les objets entre les deux bases de données (il ne peut pas être limité à certains objets et/ou utilisateurs). La signature de module permet uniquement à celui-ci TVF d'avoir l'accès inter-DB (l'utilisateur n'a pas l'autorisation, le TVF en a), et les utilisateurs qui ne peuvent pas SELECT depuis le TVF n'ont pas accès à "DatabaseB" à tout.

USE [master];

CREATE LOGIN [RestrictedUser] WITH PASSWORD = 'No way? Yes way!';
GO

---

USE [DatabaseA];

CREATE USER [RestrictedUser] FOR LOGIN [RestrictedUser];

GO
CREATE FUNCTION dbo.DataFromOtherDB()
RETURNS @Results TABLE ([SomeValue] INT)
AS
BEGIN
    INSERT INTO @Results ([SomeValue])
        SELECT [SomeValue]
        FROM   DatabaseB.dbo.LotsOfValues;

    RETURN;
END;
GO

GRANT SELECT ON dbo.[DataFromOtherDB] TO [RestrictedUser];
GO
---

USE [DatabaseB];

CREATE TABLE dbo.[LotsOfValues]
(
    [LotsOfValuesID] INT IDENTITY(1, 1) NOT NULL
        CONSTRAINT [PK_LotsOfValues] PRIMARY KEY,
    [SomeValue] INT
);

INSERT INTO dbo.[LotsOfValues] VALUES
    (1), (10), (100), (1000);
GO

---

USE [DatabaseA];

SELECT * FROM dbo.[DataFromOtherDB]();


EXECUTE AS LOGIN = 'RestrictedUser';

SELECT * FROM dbo.[DataFromOtherDB]();
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/

REVERT;

Toutes les étapes ci-dessus recréent la situation actuelle: l'utilisateur a accès à DatabaseA, a la permission d'interagir avec un objet dans DatabaseA, mais obtient une erreur car cet objet dans DatabaseA accède à quelque chose dans DatabaseB où l'utilisateur n'a aucun accès.

Les étapes ci-dessous configurent le module de chant. Il fait ce qui suit:

  1. crée un certificat dans DatabaseA
  2. Signe le TVF avec le certificat
  3. Copie le certificat (sans la clé privée) dans la base de données B
  4. Crée un utilisateur dans DatabaseB à partir du certificat
  5. Accorde SELECT l'autorisation à la table dans DatabaseB à l'utilisateur basé sur un certificat

Configuration de la signature du module:

CREATE CERTIFICATE [AccessOtherDB]
    ENCRYPTION BY PASSWORD = 'SomePassword'
    WITH SUBJECT = 'Used for accessing other DB',
    EXPIRY_DATE = '2099-12-31';

ADD SIGNATURE
    TO dbo.[DataFromOtherDB]
    BY CERTIFICATE [AccessOtherDB]
    WITH PASSWORD = 'SomePassword';

---
DECLARE @CertificatePublicKey NVARCHAR(MAX) =
            CONVERT(NVARCHAR(MAX), CERTENCODED(CERT_ID(N'AccessOtherDB')), 1);

SELECT @CertificatePublicKey AS [Cert / PublicKey]; -- debug

EXEC (N'USE [DatabaseB];
CREATE CERTIFICATE [AccessOtherDB] FROM BINARY = ' + @CertificatePublicKey + N';');
---


EXEC (N'
USE [DatabaseB];
CREATE USER [AccessOtherDbUser] FROM CERTIFICATE [AccessOtherDB];

GRANT SELECT ON dbo.[LotsOfValues] TO [AccessOtherDbUser];
');

---



EXECUTE AS LOGIN = 'RestrictedUser';

SELECT * FROM dbo.[DataFromOtherDB]();
-- Success!!

SELECT * FROM [DatabaseB].[dbo].[LotsOfValues];
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/

REVERT;

SI L'ACCÈS DOIT ÊTRE À TRAVERS UNE VUE, pour quelque raison que ce soit, vous pouvez simplement créer une vue qui sélectionne à partir du TVF montré ci-dessus. Et, dans cette situation, l'accès SELECT ne doit pas être accordé à la TVF, uniquement à la vue, comme illustré ci-dessous:

GO
CREATE VIEW dbo.[DataFromTVF]
AS
SELECT [SomeValue]
FROM   dbo.DataFromOtherDB();
GO

-- Remove direct access to the TVF as it is no longer needed:
REVOKE SELECT ON dbo.[DataFromOtherDB] FROM [RestrictedUser];

GRANT SELECT ON dbo.[DataFromTVF] TO [RestrictedUser];

Et maintenant pour le tester:

EXECUTE AS LOGIN = 'RestrictedUser';


SELECT * FROM dbo.[DataFromOtherDB]();
/*
Msg 229, Level 14, State 5, Line XXXXX
The SELECT permission was denied on the object 'DataFromOtherDB',
database 'DatabaseA', schema 'dbo'.
*/


SELECT * FROM [OwnershipChaining].[dbo].[LotsOfValues];
/*
Msg 916, Level 14, State 1, Line XXXXX
The server principal "RestrictedUser" is not able to access
the database "DatabaseB" under the current security context.
*/


SELECT * FROM dbo.[DataFromTVF];
-- Success!!


REVERT;

Pour plus d'informations sur la signature de module, veuillez visiter: https://ModuleSigning.Info/

9
Solomon Rutzky