Acessar a exibição com base na tabela em outro banco de dados sem conta nesse outro banco de dados

10

Criei a view no banco de dados1 com base em tabelas no banco de dados2. Eu dei SELECTpermissão a um usuário que tem acesso apenas ao banco de dados1. O usuário não pode fazer com que essa visualização funcione porque não possui uma conta no banco de dados2. Como posso resolver este problema? Não quero criar uma conta no banco de dados2.

tom
fonte
11
@mustaccio Não, isso não é uma duplicata dessa outra pergunta / resposta, pois a situação estava dentro do mesmo banco de dados, e esta pergunta é sobre a abrangência de bancos de dados. Por padrão, isso não é permitido. Seria necessário habilitar o encadeamento de propriedade entre bancos de dados, e isso é um enorme buraco de segurança a ser aberto para uma necessidade tão estreita.
Solomon Rutzky
11
@SolomonRutzky, eu não chamaria DB_CHAINING de "enorme falha de segurança". Em ambientes de produção típicos em que apenas membros da função sysadmin podem criar objetos, isso não é problema. Dito isso, ele deve ser usado com cuidado nos casos em que membros da função não sysadmin tenham permissões de controle em esquemas diferentes daqueles que possuem.
Dan Guzman
@ DanGuzman "Confie em mim, tudo sempre correrá conforme o planejado" não é uma estratégia eficaz. Por essa lógica, quase não há risco em definir TRUSTWORTHY ONou fazer com que o aplicativo efetue login como sa. O encadeamento de propriedade do DB e TRUSTWORTHYexiste principalmente devido a ser a única solução no momento. Mas agora, mesmo que não seja um grande risco, o DB Chaining é certamente um risco desnecessário, pois a assinatura do módulo não é tão difícil. E se alguém confiar no encadeamento de banco de dados e depois usar o SQL dinâmico, é mais provável que ele defina TRUSTWORTHY ONpara corrigi-lo, enquanto que com a assinatura do módulo não teria quebrado.
Solomon Rutzky
@SolomonRutzky, eu teria sugerido a assinatura do módulo se a pergunta fosse sobre um módulo em vez de uma exibição. Meu pensamento é que isso DB_CHAININGnão é mais arriscado do que o encadeamento de propriedade dentro do banco de dados quando os objetos deveriam estar no mesmo banco de dados.
Dan Guzman
@ DanGuzman Por que supor que "os objetos deveriam estar no mesmo banco de dados de qualquer maneira"? O OP indicou apenas o oposto, pois eles querem manter o acesso ao banco de dados separado. O fato de o OP estar usando um modo de exibição é o motivo pelo qual sugeri um TVF em vez de um procedimento armazenado, mas isso não significa que continuar usando um modo de exibição é o melhor curso de ação. É comum sugerir a modificação de estrutura e / ou abordagem quando faz sentido, como é o caso aqui. Ainda assim, adicionei um wrapper opcional à minha resposta. E, como é mais comum o dbo possuir tudo, sim, DB_CHAININGé bastante arriscado.
Solomon Rutzky

Respostas:

9

Isso é fácil de ser feito de uma maneira muito segura, usando a assinatura do módulo. Isso será semelhante às duas respostas a seguir, também aqui no DBA.StackExchange, que dão exemplos de como fazer isso:

Segurança de procedimento armazenado com execução como, consultas entre bancos de dados e assinatura de módulo

Permissões em gatilhos ao usar certificados entre bancos de dados

A diferença para essa pergunta em particular é que ela lida com uma Visualização e as Visualizações não podem ser assinadas. Portanto, você precisará alterar a Visualização em uma função com valor de tabela (TVF) de múltiplas instruções, pois elas podem ser assinadas e podem ser acessadas como uma Visualização (bem, para SELECTacesso).

O código de exemplo a seguir mostra exatamente o que está sendo solicitado na pergunta, pois o Login / Usuário "RestrictedUser" só tem acesso ao "DatabaseA" e ainda pode obter dados do "DatabaseB". Isso funciona apenas selecionando esse TVF e apenas devido ao fato de ter sido assinado.

Para realizar esse tipo de acesso ao banco de dados enquanto ainda estiver usando uma Visualização, e sem conceder permissões adicionais ao Usuário, seria necessário ativar o Encadeamento de Propriedade entre Bancos de Dados. Isso é muito menos seguro porque é completamente aberto para todos os objetos entre os dois bancos de dados (não pode ser restrito a determinados objetos e / ou usuários). A assinatura do módulo permite que apenas este SELECTTVF tenha acesso entre bancos de dados (o usuário não tem permissão, o TVF) e os usuários que não podem do TVF não têm acesso ao "Banco de DadosB".

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;

Todas as etapas acima recriam a situação atual: o usuário tem acesso ao banco de dados A, tem permissão para interagir com um objeto no banco de dados A, mas recebe um erro devido a esse objeto no banco de dados A acessando algo no banco de dados B onde o usuário não tem acesso.

Os passos abaixo configuram o canto do módulo. Faz o seguinte:

  1. cria um certificado no DatabaseA
  2. Assina o TVF com o certificado
  3. Copia o certificado (sem a chave privada) no banco de dados B
  4. Cria um usuário no banco de dadosB a partir do certificado
  5. Concede SELECTpermissão à Tabela no Banco de DadosB ao Usuário Baseado em Certificado

Configuração de assinatura do 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;

SE O ACESSO PRECISA SER ATRAVÉS DE UMA VISTA, por qualquer motivo, você pode simplesmente criar uma Visualização que selecione a partir do TVF mostrado acima. E, nessa situação, o SELECTacesso não precisa ser concedido ao TVF, apenas ao View, conforme demonstrado abaixo:

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];

E agora para testá-lo:

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 mais informações sobre a assinatura do módulo, visite: https://ModuleSigning.Info/

Solomon Rutzky
fonte
Os certificados são copiados como parte de backups regulares? Ou eles são armazenados em outro lugar e também exigem um backup do sistema de arquivos? E o que acontece se você restaurar para um ambiente inferior que possa usar senhas diferentes etc.?
Chris Aldrich
@ChrisAldrich No uso mostrado aqui, o backup é feito com o banco de dados, pois é totalmente mantido no banco de dados. Se você usar ALTER CERTIFICATE ... DROP PRIVATE KEY, a chave privada desaparecerá se você não fizer o backup primeiro em um arquivo usando BACKUP CERTIFICATE . Mas, a chave pública ainda está dentro sys.certificates. E a chave pública não precisa da senha. Somente o uso da chave privada para assinar um módulo requer a senha (que é a mesma nos servidores, diferente da proteção via chave mestra).
Solomon Rutzky