O que é Cross Database Ownership Chaining ?

Olá Pessoal,

Hoje falarei sobre o termo Cross Database Ownership Chaining. A primeira vez que vi esse termo, eu tinha pouco mais de um ano de experiência como DBA e foi na aplicação do service pack 3a do SQL Server 2000. Ao final da aplicação desse service pack, simplesmente aparecem duas opções (Apply Service Pack – Recommended e Enable Cross Database Ownership Chaining – Not Recommended). Não sei quantas vezes apliquei esse service pack nas dezenas de instalações que fiz, mas com certeza boa parte das aplicações eu não tinha muita idéia do que essa opção significava. Depois de algum tempo, acabei descobrindo sua utilidade e quando ele deve ser ou não aplicável. Embora pareça simples, boa parte da bibliografia de SQL Server (principalmente os livros de administração) deixam esse termo um pouco de lado. Também já presenciei consultores renomados tentando entender porque uma determinada SP não funcionava por um problema de permissão (a resposta estava no Cross Database Ownership Chaining).

O termo Ownership Chain era bem relevante na época do SQL Server 2000 e por muitas vezes era cobrado nos exames 70-228 e 70-229 do SQL Server 2000. Com a correta implementação de schemas no SQL Server 2005 e 2008 e a devida separação de posse (Owner) e agrupamento bem como resolução de nomes (Schemas), esse conceito perdeu um pouco o sentido quando a cadeia de propriedade (Ownership Chain) refere-se a objetos relacionados (embora seja muito relevante para instruções SQL dinâmicas). Assim sendo, não vou discorrer sobre esse tipo de situação, mas apenas quando essa cadeia refere-se a objetos de diferentes bancos.

O problema de acesso de objetos entre diferentes bancos de dados

Vamos exemplificar um problema comum entre acessos de diferentes bancos de dados. Abaixo um script TSQL que cria a situação que desejo explorar (após a execução farei as devidas explicações). O script deve ser executado por partes e não de uma só vez. Cada script deve ser executado com um login sysadmin em uma janela separada (a menos quando especificado o contrário).

— Muda o contexto de execução do banco de dados
USE Master;

— Cria quatro logins (dois de aplicação e dois de administração)
CREATE LOGIN LG1 WITH PASSWORD = ‘PWD1’, CHECK_POLICY = OFF
CREATE LOGIN LG2 WITH PASSWORD = ‘PWD2’, CHECK_POLICY = OFF
CREATE LOGIN Adm WITH PASSWORD = ‘ADM’, CHECK_POLICY = OFF
CREATE LOGIN DBA WITH PASSWORD = ‘DBA’, CHECK_POLICY = OFF

— Cria dois bancos de dados
CREATE DATABASE BD_AppX
CREATE DATABASE BD_AppY

— Atribui o Owner do Banco BD_AppX para o Login ADM
USE BD_AppX;
EXEC sp_changedbowner ‘ADM’

— Cria um usuário para LG1 no Banco BD_AppX
CREATE USER Usr1 FROM Login LG1

— Atribui o Owner do Banco BD_AppY para o Login DBA
USE BD_AppY;
EXEC sp_changedbowner ‘DBA’

— Cria um usuário para LG2 no Banco BD_AppY
CREATE USER Usr2 FROM Login LG2

O script não faz nada demais e bem autoexplicativo. Quatro logins são criados (LG1, LG2, Adm e DBA). O login Adm é Owner do banco BD_AppX e o login DBA é owner do Banco BD_AppY. O login LG1 possui direito de acessar o banco BD_AppX (por isso o usuário Usr1) e o login LG2 possui o direito de acessar o banco BD_AppY (por isso o usuário Usr2). O fato dos logins LG1 e LG2 terem respectivamente acesso aos bancos BD_AppX e BD_AppY não lhes dá direito de acessar nenhum objeto nesse banco.

Agora que as entidades (Server Principals) e recursos (Server Securables) estão criados bem como os usuários (Database Principals), vejamos como as coisas podem ficar mais interessantes ao se criar alguns recursos de banco de dados (Database Securables).

— Cria uma tabela em BD_AppX (Schema DBO)
USE BD_AppX;

CREATE TABLE Empregados (
    CodEmp INT, NomeEmp VARCHAR(100),
   
Salario SMALLMONEY, Tipo CHAR(1))

— Insere alguns registros na tabela de Empregados
INSERT INTO Empregados VALUES (1, ‘Antonio Mendes Pereira’, 15451.20, ‘I’)
INSERT INTO Empregados VALUES (2, ‘Roberto Antônio’, 21230.70, ‘I’)
INSERT INTO Empregados VALUES (3, ‘Bruno Henrique Paiva’, 12342.90, ‘I’)
INSERT INTO Empregados VALUES (4, ‘Renato Augusto’, 8900.00, ‘I’)
INSERT INTO Empregados VALUES (5, ‘Daniel Couto’, 13250.00, ‘E’)

— Cria uma View em BD_AppY (Schema DBO)
USE BD_AppY;

CREATE VIEW vEmpregadosInternos As
    SELECT CodEmp, NomeEmp FROM
    BD_AppX.dbo.Empregados WHERE
    Tipo = ‘I’

— Testa a View vEmpregados
SELECT CodEmp, NomeEmp FROM vEmpregadosInternos

— Concede permissão na View para o Usuário Usr2
GRANT SELECT ON vEmpregadosInternos TO Usr2

Esse script cria uma tabela em BD_AppX chamada Empregados cuja coluna tipo identifica se o empregado é interno (I) ou externo (E). No banco BD_AppY é criada uma View para acessar um subconjunto da tabela Empregados em BD_AppX que retorna o esperado resultado a seguir:

CodEmp NomeEmp
1 Antonio Mendes Pereira
2 Roberto Antônio
3 Bruno Henrique Paiva
4 Renato Augusto

Até então não parece haver nada de errado. Como era previsto, acessar a View simplesmente retorna o SELECT esperado de certa forma filtrando apenas os empregados internos (Tipo = ‘I’) e exibindo apenas as colunas CodEmp e NomeEmp (possivelmente para ocultar a coluna salário). Esse exemplo é bem comum se consideramos que BD_AppX é um banco de recursos humanos e que BD_AppY consome esses dados (possivelmente uma aplicação financeira, inventário, biblioteca, etc). No mundo real é bem provável que o acesso não seja feito por uma credencial SysAdmin como a utilizada para criar os scripts. É esperado que cada aplicação possui logins, usuários e permissões próprias por uma questão de segurança. O script a seguir deve ser executado com o login LG2.

— Tenta acessar a View vEmpregadosInternos
SELECT CodEmp, NomeEmp FROM vEmpregadosInternos

Embora Usr2 tenha permissão em vEmpregados, quando esse usuário faz um SELECT contra essa View, uma mensagem de erro é exibida:

Msg 916, Level 14, State 1, Line 2
The server principal "LG2" is not able to access the database "BD_AppX" under the current security context.

A mensagem informa que o login LG2 não possui permissões para acessar o banco de dados BD_AppX. Não é muito difícil entender a razão dessa mensagem aparecer. O usuário Usr2 em BD_AppY está mapeado para o login LG2 e esse login não tem acesso ao banco de dados BD_AppX já que não há um usuário em BD_AppX criado para esse login. O difícil de entender é porque essa mensagem de erro apareceu. Se o usuário Usr2 tinha permissão na View vEmpregadosInternos e essa acessa a tabela Empregados em BD_AppX por que essa mensagem apareceu ? Se o acesso na View foi concedido adequadamente não era esperado que simplesmente o usuário Usr2 conseguisse acessar os dados ? Muitos pensarão que sim, mas a verdade é que do ponto de vista de segurança esse comportamento está correto. Eis um problema relacionado ao Cross Database Ownership Chaining. Como fazer que esse acesso seja possível ?

O banco de dados BD_AppX pertence ao login ADM e o banco de dados BD_AppY pertence ao login DBA. Não seria estranho "aparecer" alguém acessando o banco BD_AppX sem que o owner ADM consentisse ou que ainda desse permissões para que isso fosse possível ? Mesmo sendo um login SysAdmin que criou a View é no mínimo estranho que um acesso desses seja feito. Imaginemos cada banco de dados como uma residência. Seria muito estranho que alguém aparecesse em uma determinada residência sem que o dono soubesse que esse alguém tivesse a permissão de ali estar. Seria uma autêntica invasão de privacidade (ainda que fosse o dono do condomínio tivesse autorizado).

Um cadeia de propriedade representa um fluxo de permissões herdadas em um conjunto de objetos de um mesmo dono (Owner). Se a frase pareceu confusa, nada melhor que uma exemplificação. Deixando o banco de dados de lado, voltemos a analogia da residência.

Supondo que o condomínio pertença ao Sr. Menezes e que o Sr. Menezes também seja dono da residência A e que o quarto 101 seja o seu quarto pessoal, qual seria a interpretação se o Sr. Menezes dissesse "Bem vindo ao meu condomínio. Fique à vontade" ? Com uma recepção dessas a interpretação é que além de usufruir dos serviços do condomínio, não haverá problema em almoçar na residência A e nem eventualmente tirar um cochilo no quarto 101 após o almoço. Afinal, Sr. Menezes é dono do condomínio e também da residência A e do quarto 101. Sendo ele dono, o simples fato dele conceder acesso irrestrito ao condomínio, faz com que implicitamente a residência e o quarto estejam disponíveis. Não é necessário dizer explicitamente "pode usar a residência A e o quatro 101", pois, o dono é o mesmo.

Agora vamos supor que a residência B pertença à Sra. Rodrigues e que o quarto 203 esteja sendo utilizado pela sua filha, a Srta. Dafne. Nesse caso, mesmo com o cortejo do Sr. Menezes, a idéia de entrar no quarto 203 sem pedir permissão à Sra. Rodrigues e a Srta. Dafne é sem dúvida uma enorme falta de educação. Ainda que a Sra. Rodrigues consentisse, em última instância, a dona do quatro é a Srta. Dafne que certamente deve ser consultada sobre a utilização do quarto. Também não podemos dizer que só o consentimento dela seja suficiente. Sem que a Sra. Rodrigues permita, não parece ser correto entrar na residência B.

Na primeira situação, dizemos que a cadeia de propriedade não foi violada, pois, o Owner é o mesmo (Sr. Rodrigues) e por isso, se ele consentiu, ou seja, deu as devidas permissões, nenhum ajuste é necessário para acessar os recursos desejados (a residência A e o quarto 101). No segundo exemplo, houve uma quebra na cadeia de propriedade já que o Owner de uma propriedade maior (o condomínio) não é o mesmo das propriedades menores (a residência B e o quarto 203). Quando há quebra na cadeia de propriedade (Broker Ownership Chain) é necessário obter permissões explícitas para acessar os recursos necessários. Nesse caso, não se pode ir ao quatro 203 sem o aval da dona da residência (Sra. Rodrigues) e da dona do quatro (Srta. Dafne).

Essa é exatamente a mesma situação representada nos bancos de dados e objetos criados. O banco de dados BD_AppX pertence ao login ADM e o banco de dados BD_AppY pertence ao login DBA. Quando a view foi criada, um login SysAdmin que acessou a View. O fato do login ser SysAdmin lhe dá permissões totais e não haverá nenhum problema (no exemplo é como se fosse um policial chegando ao condomínio com um mandato). Quando login que não sejam sysadmin são utilizados (típicos de aplicação) então haverá uma quebra na cadeia de propriedade, pois, os Owners dos bancos são diferentes. Nesse caso, o acesso à view vEmpregadosInternos falha.

Superando os problemas de acesso a objetos em bancos de dados diferentes

Uma das formas de fazer com que a View vEmpregadosInternos retorne os dados corretamente é conceder permissões na tabela Empregados. Afinal se houve uma quebra na cadeia de propriedade é necessário dar permissões adicionais.

— Muda o contexto do banco de dados para BD_AppX
USE BD_AppX;

— Cria um usuário para o login LG2 em BD_AppX
CREATE USER Usr2 FROM Login LG2

— Concede permissões na tabela Empregados
GRANT SELECT ON Empregados TO Usr2

Após executar o script, é possível ao login LG2 retornar os dados da View vEmpregadosInternos sem maiores problemas.

— Tenta acessar a View vEmpregadosInternos
SELECT CodEmp, NomeEmp FROM vEmpregadosInternos

De fato, o SELECT acima executado por LG2 em BD_AppY produziu o resultado esperado.

CodEmp NomeEmp
1 Antonio Mendes Pereira
2 Roberto Antônio
3 Bruno Henrique Paiva
4 Renato Augusto

Embora a View funcione como "deveria", essa solução acaba de gerar um grave problema de segurança. Da forma como está elaborada, ele permite que o Login LG2 acesse a tabela diretamente sem utilizar a View. Afinal, LG2 ganhou permissões explícitas sobre a tabela de empregados e agora está livre para consultar não somente os empregados internos (Tipo = ‘I’) como também visualizar o salário de todos os empregados. Se por um lado as permissões permitiram o acesso à View, permitiram acessos bem maiores do que os desejados.

O uso do Cross Database Ownership Chain

O uso do Cross Database Ownership Chain permite que se consiga uma solução que permita o acesso à view, mas sem abrir brechas de segurança tão aparentes. Ao invés de conceder as permissões explícitas para superar a quebra da cadeia de propriedade, pode-se simplesmente evitar que ela seja quebrada, tornando todos os bancos subordinados ao mesmo Owner.

— Retira o acesso de LG2 em BD_AppX (Faz com que LG2 perca a permissão em Empregados)
USE BD_AppX;

DROP USER Usr2

— Torna o usuário DBA Owner de BD_AppX
EXEC sp_changedbowner ‘DBA’

— Cria um usuário para LG2 no Banco BD_AppX (sem direito à nada)
CREATE USER Usr2 FROM Login LG2

— Habilita o uso do Cross Database OwnerShip Chaining em BD_AppX
ALTER DATABASE BD_AppX SET DB_CHAINING ON

— Habilita o uso do Cross Database OwnerShip Chaining em BD_AppY
ALTER DATABASE BD_AppX SET DB_CHAINING ON

Se LG2 tentar executar um SELECT contra vEmpregadosInternos, o acesso será permitido. Se LG2 tentar executar um SELECT diretamente contra Empregados, mesmo com que possua acesso ao banco BD_AppX, não conseguirá efetuar o SELECT.

Msg 229, Level 14, State 5, Line 2
The SELECT permission was denied on the object ‘Empregados’, database ‘BD_AppX’, schema ‘dbo’.

Isso garante um nível mais seguro de aplições que acessem mais de um banco de dados de forma a combinar informações entre diferentes bancos de dados. Ainda assim, embora um pouco mais seguro que conceder as permissões explícitas, esse tipo de solução está aberto à alguns problemas de segurança. Se a role public tiver permissões, LG2 herdará essas permissões, se o SQL Server 2000 for utilizado, LG2 poderá ter acesso aos metadados de BD_AppX entre outros problemas. Embora o Cross Database Ownership Chaining possibilite o desenvolvimento de aplicações um pouco mais robustas do ponto de vista de informação há outras soluções mais seguras a serem trabalhadas. Abordarei algumas dessas nos próximos artigos.

[ ]s,

Gustavo

4 Respostas para “O que é Cross Database Ownership Chaining ?

  1. Excentelente artigo Gustavo.Coincidentemente darei uma aula amanhã na qual falarei sobre segurança no SQL Server. Entre outros pontos, o Ownership Chains será discutido. Como você mencionou, isso chega a ser uma faca de dois legumes (hehe) e não tenho duvidas de que na versão 2000 é muito útil.Porém, a partir das novas versões isso de fato perdeu um pouco sentido, já que há formas mais interessantes de fazer esse controle.Mais uma vez, parabéns pelo artigo.Grande abraço.Thiago Caserta.

  2. Olá Thiago,Puxa mas que coincidência hein ? Bem, eu fiz a "segunda parte" mas que pena que a aula já foi…Abs,

  3. Muito bem explicado, como sempre.
    Valeu!

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s