Row-level security: controle de acesso granular

1. Introdução à Row-Level Security (RLS)

1.1. O que é RLS e por que ela é necessária

Row-Level Security (RLS) é um mecanismo de segurança de banco de dados que permite controlar o acesso a linhas individuais de uma tabela com base nas características do usuário ou da sessão. Diferentemente dos modelos tradicionais de permissão (que operam no nível de tabela ou coluna), o RLS aplica filtros automáticos e invisíveis ao usuário, garantindo que cada consulta retorne apenas as linhas que o usuário tem autorização para ver.

A necessidade do RLS surge em cenários onde diferentes usuários compartilham a mesma tabela física, mas precisam enxergar subconjuntos distintos de dados. Sem RLS, seria necessário criar views separadas ou implementar lógica de filtragem no código da aplicação, aumentando a complexidade e o risco de falhas de segurança.

1.2. Diferença entre RLS e permissões tradicionais

Permissões tradicionais (GRANT/REVOKE) operam no nível de objeto: um usuário pode ou não acessar uma tabela inteira, ou colunas específicas. Já o RLS opera no nível de linha: mesmo que um usuário tenha permissão total sobre uma tabela, ele só enxergará as linhas que a política de segurança permitir.

Aspecto Permissões Tradicionais RLS
Escopo Tabela/Coluna Linha individual
Controle Binário (tem/não tem acesso) Granular (filtro dinâmico)
Implementação GRANT/REVOKE Políticas + Funções predicado

1.3. Casos de uso típicos

  • Multi-tenancy: aplicações SaaS onde cada cliente (tenant) possui dados isolados na mesma tabela
  • Compliance (LGPD/GDPR): garantir que usuários vejam apenas dados pessoais aos quais têm direito
  • Dados departamentais: funcionários de RH veem apenas registros de seu departamento
  • Hierarquia organizacional: gerentes veem dados de sua equipe, diretores veem dados de toda a empresa

2. Conceitos Fundamentais do RLS

2.1. Políticas de segurança

Uma política de segurança é uma regra que define quais linhas são visíveis ou modificáveis para um determinado papel de banco de dados. Cada política está associada a uma tabela específica e pode ser aplicada a operações SELECT, INSERT, UPDATE e DELETE de forma independente.

2.2. Funções de predicado

As funções de predicado são expressões SQL que retornam verdadeiro ou falso para cada linha. Quando uma consulta é executada, o banco de dados adiciona automaticamente o predicado da política à cláusula WHERE, filtrando as linhas que não atendem à condição.

2.3. Contexto de sessão e variáveis de aplicação

O RLS utiliza informações do contexto da sessão para determinar quais linhas devem ser exibidas. As principais variáveis disponíveis são:

  • current_user: nome do usuário do banco de dados
  • current_setting('app.tenant_id'): variável de aplicação configurada na sessão
  • Funções customizadas que retornam o identificador do usuário logado

3. Implementação Prática de RLS no PostgreSQL

3.1. Habilitando RLS em uma tabela

-- Criação da tabela exemplo
CREATE TABLE pedidos (
    id SERIAL PRIMARY KEY,
    cliente_id INTEGER NOT NULL,
    valor DECIMAL(10,2),
    status VARCHAR(20)
);

-- Habilita RLS na tabela
ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY;

3.2. Criando políticas com CREATE POLICY

-- Política que permite ao usuário ver apenas seus próprios pedidos
CREATE POLICY pedidos_usuario_policy ON pedidos
    FOR SELECT
    USING (cliente_id = current_user_id());

-- Política para INSERT com validação (WITH CHECK)
CREATE POLICY pedidos_insert_policy ON pedidos
    FOR INSERT
    WITH CHECK (cliente_id = current_user_id());

3.3. Políticas para diferentes operações

-- Política para UPDATE: só permite alterar pedidos pendentes do próprio usuário
CREATE POLICY pedidos_update_policy ON pedidos
    FOR UPDATE
    USING (cliente_id = current_user_id() AND status = 'PENDENTE')
    WITH CHECK (cliente_id = current_user_id());

-- Política para DELETE: apenas administradores podem excluir
CREATE POLICY pedidos_delete_policy ON pedidos
    FOR DELETE
    USING (current_user = 'admin');

4. Gerenciamento de Múltiplos Tenants com RLS

4.1. Estrutura de tabela com coluna tenant_id

CREATE TABLE clientes (
    id SERIAL PRIMARY KEY,
    tenant_id INTEGER NOT NULL,
    nome VARCHAR(100),
    email VARCHAR(100)
);

ALTER TABLE clientes ENABLE ROW LEVEL SECURITY;

4.2. Configuração de variáveis de sessão por aplicação

-- Função para obter o tenant_id da sessão
CREATE OR REPLACE FUNCTION get_tenant_id()
RETURNS INTEGER AS $$
BEGIN
    RETURN current_setting('app.tenant_id')::INTEGER;
EXCEPTION
    WHEN OTHERS THEN RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- Política baseada na variável de sessão
CREATE POLICY clientes_tenant_policy ON clientes
    FOR ALL
    USING (tenant_id = get_tenant_id());

4.3. Testando isolamento entre tenants

-- Sessão do Tenant 1
SET app.tenant_id = 1;
SELECT * FROM clientes;  -- Retorna apenas clientes do tenant 1

-- Sessão do Tenant 2
SET app.tenant_id = 2;
SELECT * FROM clientes;  -- Retorna apenas clientes do tenant 2

5. RLS em Ações: Exemplos com Código

5.1. Política simples: filtrar por usuário logado

-- Tabela de tarefas
CREATE TABLE tarefas (
    id SERIAL PRIMARY KEY,
    usuario_responsavel VARCHAR(50),
    descricao TEXT,
    concluida BOOLEAN DEFAULT FALSE
);

ALTER TABLE tarefas ENABLE ROW LEVEL SECURITY;

CREATE POLICY tarefas_usuario_policy ON tarefas
    FOR SELECT
    USING (usuario_responsavel = current_user);

5.2. Política com subconsulta

-- Tabela de permissões auxiliar
CREATE TABLE permissoes_projeto (
    usuario VARCHAR(50),
    projeto_id INTEGER
);

-- Política que verifica permissão em tabela auxiliar
CREATE POLICY documentos_projeto_policy ON documentos
    FOR SELECT
    USING (projeto_id IN (
        SELECT projeto_id FROM permissoes_projeto
        WHERE usuario = current_user
    ));

5.3. Política para UPDATE garantindo registros próprios

CREATE POLICY tarefas_update_policy ON tarefas
    FOR UPDATE
    USING (usuario_responsavel = current_user AND NOT concluida)
    WITH CHECK (usuario_responsavel = current_user);

6. Desempenho e Boas Práticas

6.1. Impacto na performance

O RLS adiciona overhead a cada consulta, pois o predicado da política é avaliado para todas as linhas. Para minimizar o impacto:

  • Crie índices nas colunas usadas nas políticas (ex: tenant_id, usuario_responsavel)
  • Use EXPLAIN ANALYZE para verificar se os índices estão sendo utilizados
  • Evite subconsultas complexas nas políticas

6.2. Evitando conflitos com triggers

-- Verifique se triggers não contornam as políticas
-- Exemplo: trigger que insere dados sem respeitar WITH CHECK
CREATE OR REPLACE FUNCTION trigger_insercao_auditoria()
RETURNS TRIGGER AS $$
BEGIN
    -- Ação que pode burlar a política
    INSERT INTO auditoria VALUES (NEW.*);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

6.3. Monitoramento e depuração

-- Visualizar políticas existentes
SELECT * FROM pg_policies WHERE tablename = 'pedidos';

-- Analisar plano de execução com RLS ativo
EXPLAIN ANALYZE SELECT * FROM pedidos;

7. RLS vs. Alternativas de Controle de Acesso

7.1. Comparação com views filtradas

Aspecto RLS Views Filtradas
Manutenção Centralizada no banco Requer criação de múltiplas views
Performance Otimizador pode usar índices Pode ter overhead de materialização
Flexibilidade Políticas dinâmicas por operação Estáticas por definição

7.2. Comparação com permissões no backend

  • RLS: segurança no banco de dados, não pode ser contornada pela aplicação
  • Backend: flexível, mas vulnerável a erros de implementação

7.3. Quando NÃO usar RLS

  • Cenários de altíssima concorrência onde o overhead é crítico
  • Dados não sensíveis onde o controle granular é desnecessário
  • Bancos de dados que não suportam RLS nativamente (MySQL, SQL Server até versões recentes)

8. Considerações Finais e Próximos Passos

8.1. Resumo dos benefícios

O RLS oferece uma camada adicional de segurança que opera diretamente no banco de dados, garantindo que mesmo que a aplicação seja comprometida, os dados permaneçam protegidos. É especialmente valioso em arquiteturas multi-tenant e ambientes regulados.

8.2. Integração com outros temas

  • Audit logging: combine RLS com triggers de auditoria para rastrear acessos
  • Encryption: use RLS para controlar acesso a colunas com dados criptografados
  • Backup e restore: políticas RLS são preservadas durante operações de backup

8.3. Próximos passos

Para aprofundamento, explore:
- Políticas hierárquicas (gerente vendo dados da equipe)
- RLS com funções imutáveis para melhor performance
- Testes de segurança automatizados com RLS

Referências