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

10

J'ai créé une vue dans database1 basée sur des tables dans database2. J'ai donné la SELECTpermission à 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.

à M
la source
1
@mustaccio Non, ce n'est pas un doublon de cette autre question / réponse car cette situation se trouvait dans la même base de données, et cette question concerne les bases de données étendues. Par défaut, cela n'est pas autorisé. Il faudrait activer le chaînage de la propriété de bases de données croisées, et c'est un énorme trou de sécurité à ouvrir pour un besoin aussi étroit.
Solomon Rutzky
1
@SolomonRutzky, je n'appellerais pas DB_CHAINING un "énorme trou de sécurité". Dans les environnements de production typiques où seuls les membres du rôle administrateur système peuvent créer des objets, ce n'est pas un problème. Cela dit, il doit être utilisé avec précaution dans les cas où les membres de rôle non administrateur système ont des autorisations de contrôle sur des schémas autres que ceux qu'ils possèdent.
Dan Guzman
@DanGuzman "Croyez-moi, tout se passera toujours comme prévu" n'est pas une stratégie efficace. Selon cette logique, il n'y a presque aucun risque de définir TRUSTWORTHY ONou de faire en sorte que l'application se connecte sous sa. DB Ownership Chaining et TRUSTWORTHYexistent principalement en raison d'être la seule solution à l'époque. Mais maintenant, même si ce n'est pas un risque énorme, DB Chaining est certainement un risque inutile car la signature de module n'est pas si difficile. Et si l'on s'appuie sur le chaînage de bases de données et utilise ensuite Dynamic SQL, ils sont plus susceptibles TRUSTWORTHY ONde le régler, alors qu'avec la signature de module, il ne se serait pas cassé.
Solomon Rutzky
@SolomonRutzky, j'aurais suggéré la signature d'un module si la question concernait un module plutôt qu'une vue. Ma pensée est que ce DB_CHAININGn'est pas plus risqué que le chaînage de propriété intra-base de données alors que les objets devraient de toute façon être dans la même base de données.
Dan Guzman
@DanGuzman Pourquoi supposer que "les objets auraient de toute façon dû être dans la même base de données"? L'OP n'a indiqué le contraire que dans la mesure où il souhaite conserver l'accès DB séparé. Le fait que l'OP utilise une vue est la raison pour laquelle j'ai suggéré une TVF au lieu d'une procédure stockée, mais cela ne signifie pas que continuer à utiliser une vue est la meilleure solution. Il est courant de suggérer de modifier la structure et / ou l'approche quand cela a du sens de le faire, comme c'est le cas ici. Pourtant, j'ai ajouté un wrapper optionnel View à ma réponse. Et, étant donné qu'il est plus courant que "dbo" possède tout, oui, DB_CHAININGc'est assez risqué.
Solomon Rutzky

Réponses:

9

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

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 de bases de données croisées

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' SELECTaccès).

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 ne fonctionne qu'en sélectionnant celui-ci TVF , et uniquement en raison de sa signature.

La réalisation de ce type d'accès entre bases de données tout en utilisant une vue et en ne donnant à l'utilisateur aucune autorisation supplémentaire, nécessiterait l'activation du 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 à ce seul TVF d'avoir un accès cross-DB (l'utilisateur n'a pas l'autorisation, le TVF en a), et les utilisateurs qui ne peuvent pas SELECTdepuis le TVF n'ont aucun accès à "DatabaseB".

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 la 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 l' SELECTautorisation à 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 une raison quelconque, vous pouvez simplement créer une vue qui sélectionne à partir du TVF illustré ci-dessus. Et, dans cette situation, l' SELECTaccès n'a pas besoin d'être accordé à la TVF, mais 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/

Solomon Rutzky
la source
Les certificats sont-ils sauvegardés dans le cadre de sauvegardes régulières? Ou sont-ils stockés ailleurs et nécessitent également une sauvegarde du système de fichiers? Et que se passe-t-il si vous restaurez dans un environnement inférieur qui peut utiliser des mots de passe différents, etc.?
Chris Aldrich
@ChrisAldrich Dans l'utilisation illustrée ici, il est sauvegardé avec la base de données car il est entièrement conservé dans la base de données. Si vous utilisez, ALTER CERTIFICATE ... DROP PRIVATE KEYla clé privée disparaîtra si vous ne la sauvegardez pas d'abord dans un fichier à l'aide du CERTIFICAT DE SAUVEGARDE . Mais, la clé publique est toujours en place sys.certificates. Et la clé publique n'a pas besoin du mot de passe. Seule l'utilisation de la clé privée pour signer un module nécessite le mot de passe (qui est le même sur tous les serveurs, contrairement à la protection via la clé principale).
Solomon Rutzky