Vista de acceso basada en la tabla en otra base de datos sin cuenta en esa otra base de datos

10

Creé la vista en la base de datos1 basada en tablas en la base de datos2. Le di SELECTpermiso a un usuario que solo tiene acceso a la base de datos1. El usuario no puede hacer que esta vista funcione porque no tiene una cuenta en la base de datos2. ¿Cómo puedo resolver este problema? No quiero crear una cuenta en la base de datos2.

tom
fuente
1
@mustaccio No, este no es un duplicado de esa otra pregunta / respuesta ya que esa situación estaba dentro de la misma base de datos, y esta pregunta se trata de abarcar bases de datos. Por defecto eso no está permitido. Uno tendría que habilitar el encadenamiento de propiedad entre bases de datos, y ese es un gran agujero de seguridad para abrir ante una necesidad tan limitada.
Solomon Rutzky
1
@SolomonRutzky, no llamaría a DB_CHAINING un "gran agujero de seguridad". En entornos de producción típicos donde solo los miembros del rol sysadmin pueden crear objetos, no es un problema. Dicho esto, debe usarse con cuidado en los casos en que los miembros del rol que no son administradores del sistema tienen permisos de control en esquemas distintos de los que poseen.
Dan Guzman
@DanGuzman "Confía en mí, todo siempre irá de acuerdo al plan" no es una estrategia efectiva. Según esa lógica, casi no hay riesgo al configurar TRUSTWORTHY ONo hacer que la aplicación inicie sesión como sa. DB Ownership Chaining y TRUSTWORTHYexisten principalmente debido a que son la única solución en ese momento. Pero ahora, incluso si no es un gran riesgo, DB Chaining es ciertamente un riesgo innecesario ya que la firma de módulos no es tan difícil. Y si uno se basa en el encadenamiento de la base de datos y luego usa SQL dinámico, es más probable que lo configuren TRUSTWORTHY ONpara solucionarlo, mientras que con la firma del módulo no se hubiera roto.
Solomon Rutzky
@SolomonRutzky, habría sugerido la firma del módulo si la pregunta fuera sobre un módulo en lugar de una vista. Mi opinión es que DB_CHAININGno es más arriesgado que el encadenamiento de propiedad dentro de la base de datos cuando los objetos deberían haber estado en la misma base de datos de todos modos.
Dan Guzman
@DanGuzman ¿Por qué asumir que "los objetos deberían haber estado en la misma base de datos de todos modos"? El OP solo ha indicado lo contrario, ya que quieren mantener el acceso a la base de datos separado. El hecho de que el OP esté usando una Vista es la razón por la que sugerí un TVF en lugar de un Procedimiento almacenado, pero eso no significa que continuar usando una Vista sea el mejor curso de acción. Es común sugerir modificar la estructura y / o el enfoque cuando tiene sentido hacerlo, como es el caso aquí. Aún así, agregué una vista de contenedor opcional a mi respuesta. Y, dado que es más común que "dbo" sea dueño de todo, sí, DB_CHAININGes bastante arriesgado.
Solomon Rutzky

Respuestas:

9

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 SELECTacceder).

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 SELECTdesde 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:

  1. crea un certificado en la base de datosA
  2. Firma el TVF con el Certificado
  3. Copia el certificado (sin la clave privada) a la base de datos B
  4. Crea un usuario en la base de datos B a partir del certificado
  5. Otorga SELECTpermiso 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, SELECTno 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/

Solomon Rutzky
fuente
¿Se respaldan los certificados como parte de las copias de seguridad regulares? ¿O están almacenados en otro lugar y requieren una copia de seguridad del sistema de archivos también? ¿Y qué sucede si restaura a un entorno inferior que puede usar contraseñas diferentes, etc.?
Chris Aldrich
@ChrisAldrich En el uso que se muestra aquí, está respaldado con la base de datos, ya que se encuentra completamente dentro de la base de datos. Si lo usa ALTER CERTIFICATE ... DROP PRIVATE KEY, la clave privada desaparecería si no la respaldara primero en un archivo usando BACKUP CERTIFICATE . Pero, la clave pública todavía está en sys.certificates. Y la clave pública no necesita la contraseña. Solo el uso de la clave privada para firmar un módulo requiere la contraseña (que es la misma en todos los servidores, a diferencia de cuando se protege mediante una clave maestra).
Solomon Rutzky