Pular para o conteúdo
← Notas técnicas

Banco de dados

Isolamento multi-tenant com RLS em PostgreSQL — o que quebra em testes e por quê

Papel em tabela separada, função security definer e uma suíte que tenta vazar dado entre tenants de propósito.

Row Level Security move o isolamento para onde ele resiste a erro de aplicação. Um filtro esquecido em uma consulta não vaza dado se a política do banco não deixar. Isso só vale se as políticas forem escritas com cuidado, e há dois erros que aparecem quase sempre.

Erro 1: papel guardado na tabela de perfil

Se o papel do usuário está em uma coluna da tabela que o próprio usuário pode atualizar, o sistema tem escalonamento de privilégio por design. Papel vive em tabela própria, com chave única por usuário e papel, sem política de escrita para o usuário comum.

Erro 2: política que consulta a própria tabela

Uma política que precisa ler outra tabela protegida por RLS entra em recursão ou avalia com o contexto errado. A saída é uma função security definer, estável, com search_path fixo, que faz a checagem fora do alcance da política.

create or replace function public.tem_papel(_user_id uuid, _papel app_role)
returns boolean
language sql
stable
security definer
set search_path = public
as $$
  select exists (
    select 1 from public.user_roles
    where user_id = _user_id and role = _papel
  )
$$;

A suíte que precisa existir

Teste de RLS que só verifica o caminho feliz não prova nada. O teste útil autentica como o tenant A e tenta ler, escrever, atualizar e apagar linha do tenant B, em cada tabela, por cada papel. Ele falha o build quando qualquer uma dessas operações retorna linha ou sucesso.

  • Leitura cruzada direta em cada tabela.
  • Leitura cruzada por junção — o vazamento costuma aparecer aqui, não na consulta simples.
  • Escrita com id de outro tenant no corpo da requisição.
  • Consulta agregada: count e sum vazam existência mesmo sem devolver linha.
  • Acesso por função e por view, que podem carregar security definer sem que ninguém tenha pensado nisso.

Também vale conferir os GRANTs. RLS sem GRANT bloqueia legítimo; GRANT sem RLS libera tudo. Os dois precisam estar na mesma migração, na mesma revisão.

Próximo passo

Vale ocupar uma hora da sua agenda?

Se o problema é visão computacional, dado ou decisão automatizada que precisa sustentar auditoria, a conversa começa direto comigo. Sem camada intermediária.

Agendar conversa