Esto es fácil de lograr de una manera muy segura usando la firma de módulos. Esto será similar a las siguientes dos respuestas mías, también aquí en DBA.StackExchange, que dan ejemplos de hacer exactamente esto:
Seguridad de procedimientos almacenados con ejecución como, consultas cruzadas de bases de datos y firma de módulos
Permisos en disparadores cuando se usan certificados de bases de datos cruzadas
La diferencia para esta pregunta en particular es que trata con una Vista, y las Vistas no se pueden firmar. Por lo tanto, deberá cambiar la Vista a una Función de valor de tabla de varias instrucciones (TVF), ya que se puede firmar y acceder a ellos como una Vista (bueno, para SELECT
acceder).
El siguiente código de ejemplo muestra cómo hacer exactamente lo que se solicita en la pregunta, ya que el "Usuario restringido" de inicio de sesión / Usuario solo tiene acceso a "Base de datos A" y aún puede obtener datos de "Base de datos B". Esto funciona solo seleccionando de este TVF , y solo debido a que está firmado.
Lograr este tipo de acceso a la base de datos cruzada mientras se sigue utilizando una Vista, y sin otorgarle al Usuario ningún permiso adicional, requeriría habilitar el Encadenamiento de propiedad de la base de datos cruzada. Eso es mucho menos seguro porque es completamente abierto para todos los objetos entre ambas bases de datos (no se puede restringir a ciertos objetos y / o usuarios). La firma de módulos permite que solo este TVF tenga acceso cruzado a la base de datos (el usuario no tiene el permiso, el TVF sí), y los usuarios que no pueden SELECT
desde el TVF no tienen acceso a la "Base de datos B".
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;
Todos los pasos anteriores recrean la situación actual: el usuario tiene acceso a la base de datos A, tiene permiso para interactuar con un objeto en la base de datos A, pero obtiene un error debido a que ese objeto en la base de datos A accede a algo en la base de datos B donde el usuario no tiene ningún acceso.
Los siguientes pasos configuran el canto del módulo. Hace lo siguiente:
- crea un certificado en la base de datosA
- Firma el TVF con el Certificado
- Copia el certificado (sin la clave privada) a la base de datos B
- Crea un usuario en la base de datos B a partir del certificado
- Otorga
SELECT
permiso a la tabla en la base de datos B al usuario basado en certificado
Configuración de firma del módulo:
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 EL ACCESO DEBE SER A TRAVÉS DE UNA VISTA, por cualquier razón, simplemente puede crear una Vista que seleccione desde el TVF que se muestra arriba. Y, en esa situación, SELECT
no es necesario otorgar acceso al TVF, solo a la Vista, como se demuestra a continuación:
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];
Y ahora para probarlo:
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;
Para obtener más información sobre la firma de módulos, visite: https://ModuleSigning.Info/