Implementar segurança em nível de linha

Concluído

A segurança em nível de linha (RLS) não usa criptografia e opera no nível do banco de dados para restringir o acesso a uma tabela usando uma diretiva de segurança baseada na associação ao grupo ou no contexto de autorização. Isto funcionalmente equivale a uma WHERE cláusula.

A diretiva de segurança invoca uma função com valor de tabela embutido para proteger o acesso às linhas de uma tabela.

Dependendo do atributo de um usuário, o predicado determina se esse usuário tem acesso às informações relevantes. Quando você executa uma consulta em uma tabela, a diretiva de segurança aplica a função de predicado. Dependendo dos requisitos de negócios, a RLS pode ser tão simples WHERE CustomerId = 29 ou complexa quanto necessário.

Há dois tipos de diretivas de segurança suportadas pela segurança em nível de linha:

  • Predicados de filtro - restringe o acesso a dados que violam o predicado.

    Acesso Definição
    SELECT Não é possível visualizar linhas filtradas.
    UPDATE Não é possível atualizar linhas filtradas.
    DELETE Não é possível excluir linhas filtradas.
    INSERT Não aplicável.
  • Bloquear predicados - restringir alterações de dados que violem o predicado.

    Acesso Definição
    APÓS INSERIR Impede que os usuários insiram linhas com valores que violam o predicado.
    APÓS A ATUALIZAÇÃO Impede que os usuários atualizem linhas para valores que violam o predicado.
    ANTES DA ATUALIZAÇÃO Impede que os usuários atualizem linhas que atualmente violam o predicado.
    ANTES DE ELIMINAR Bloqueia operações de exclusão se a linha violar o predicado.

Como o controle de acesso é configurado e aplicado no nível do banco de dados, as alterações no aplicativo são mínimas, se houver. Além disso, os usuários podem ter acesso direto às tabelas e podem consultar seus próprios dados.

A segurança em nível de linha é implementada em três etapas principais:

  1. Crie os usuários ou grupos que você deseja isolar o acesso.
  2. Crie a função com valor de tabela embutido que filtrará os resultados com base no predicado definido.
  3. Crie uma política de segurança para a tabela, atribuindo a função criada acima.

Os comandos T-SQL abaixo demonstram como usar a RLS em um cenário em que o acesso do usuário é segregado por locatário:

-- Create supporting objects for this example
CREATE TABLE [Sales] (SalesID INT, 
    ProductID INT, 
    TenantName NVARCHAR(10), 
    OrderQtd INT, 
    UnitPrice MONEY)
GO

INSERT INTO [Sales]  VALUES (1, 3, 'Tenant1', 5, 10.00);
INSERT INTO [Sales]  VALUES (2, 4, 'Tenant1', 2, 57.00);
INSERT INTO [Sales]  VALUES (3, 7, 'Tenant1', 4, 23.00);
INSERT INTO [Sales]  VALUES (4, 2, 'Tenant2', 2, 91.00);
INSERT INTO [Sales]  VALUES (5, 9, 'Tenant3', 5, 80.00);
INSERT INTO [Sales]  VALUES (6, 1, 'Tenant3', 5, 35.00);
INSERT INTO [Sales]  VALUES (7, 3, 'Tenant4', 8, 11.00);

-- View all the rows in the table  
SELECT * FROM Sales;

Em seguida, crie os usuários e conceda-lhes acesso à tabela Sales . Neste exemplo, cada usuário é responsável por um locatário específico. O usuário TenantAdmin tem acesso para ver os dados de todos os locatários.

CREATE USER [TenantAdmin] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant1] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant2] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant3] WITH PASSWORD = '<strong password>'
GO
CREATE USER [Tenant4] WITH PASSWORD = '<strong password>'
GO

GRANT SELECT ON [Sales] TO [TenantAdmin]
GO
GRANT SELECT ON [Sales] TO [Tenant1]
GO
GRANT SELECT ON [Sales] TO [Tenant2]
GO
GRANT SELECT ON [Sales] TO [Tenant3]
GO
GRANT SELECT ON [Sales] TO [Tenant4]
GO

Em seguida, criaremos um novo esquema, uma função com valor de tabela embutido e concederemos ao usuário acesso à nova função. O WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin' predicado avalia se o nome de usuário que executa a consulta corresponde aos valores da coluna TenantName .

CREATE SCHEMA sec;  
GO  

--Create the filter predicate

CREATE FUNCTION sec.tvf_SecurityPredicatebyTenant(@TenantName AS NVARCHAR(10))  
    RETURNS TABLE  
WITH SCHEMABINDING  
AS  
    RETURN	SELECT 1 AS result
			WHERE @TenantName = USER_NAME() OR USER_NAME() = 'TenantAdmin';  
GO

--Grant users access to inline table-valued function

GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [TenantAdmin]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant1]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant2]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant3]
GO
GRANT SELECT ON sec.tvf_SecurityPredicatebyTenant TO [Tenant4]
GO

--Create security policy and add the filter predicate
CREATE SECURITY POLICY sec.SalesPolicy  
ADD FILTER PREDICATE sec.tvf_SecurityPredicatebyTenant(TenantName) ON [dbo].[Sales]
WITH (STATE = ON);  
GO

Neste ponto, estamos prontos para testar o acesso:

EXECUTE AS USER = 'TenantAdmin';  
SELECT * FROM dbo.Sales;
REVERT;  
  
EXECUTE AS USER = 'Tenant1';  
SELECT * FROM dbo.Sales;
REVERT;  
  
EXECUTE AS USER = 'Tenant2';  
SELECT * FROM dbo.Sales;
REVERT;

EXECUTE AS USER = 'Tenant3';  
SELECT * FROM dbo.Sales;
REVERT;

EXECUTE AS USER = 'Tenant4';  
SELECT * FROM dbo.Sales;
REVERT;

O usuário TenantAdmin deve ver todas as linhas. Os usuários Tenant1, Tenant2, Tenant3 e Tenant4 só devem ver suas próprias linhas.

Se você alterar a política de segurança com WITH (STATE = OFF);o , notará que os usuários verão todas as linhas.

Screenshot of T-SQL commands to alter a security policy.

Nota

Há um risco de vazamento de informações se um invasor escrever uma consulta com uma cláusula especialmente criada WHERE e, por exemplo, um erro de divisão por zero, para forçar uma exceção se a WHERE condição for verdadeira. Isso é conhecido como um ataque de canal lateral. É aconselhável limitar a capacidade dos usuários de executar consultas ad hoc ao usar a segurança em nível de linha.

Caso de utilização

A segurança em nível de linha é ideal para muitos cenários, incluindo:

  • Quando você precisa isolar o acesso departamental no nível da linha.
  • Quando você precisa restringir o acesso aos dados dos clientes apenas aos dados relevantes para sua empresa.
  • Quando você precisa restringir o acesso para fins de conformidade.

Melhor prática

Aqui estão algumas práticas recomendadas a serem consideradas ao implementar a RLS:

  • É recomendável criar um esquema separado para funções de predicados e políticas de segurança.
  • Sempre que possível, evite conversões de tipo em funções de predicados.
  • Para maximizar o desempenho, evite o uso excessivo de junções de tabela e recursão em funções de predicados.