Brasil 12:18 UTC−3·squads trabalhando agora
NDA antes de tudocódigo 100% seuLGPD · GDPR
<alkwar.com/pt-br/insights/saas-multi-tenant-postgresql-rls

SaaS multi-tenant com row-level security no PostgreSQL

Quando todos os clientes dividem as mesmas tabelas, um WHERE esquecido pode mostrar os dados de uma empresa para outra. O row-level security tira essa regra da aplicação e coloca no banco.

AUTOR[ PLACEHOLDER · Autor ]
PUBLICADO[ Data ]
LEITURA8 min

01Por que o banco deve garantir o isolamento

Num SaaS com schema compartilhado, toda tabela tem uma coluna tenant_id e toda consulta precisa filtrar por ela. A aplicação consegue fazer isso, mas precisa fazer sempre, em toda consulta, escrita por todo engenheiro. O row-level security (RLS) faz o próprio PostgreSQL aplicar o filtro, e uma cláusula esquecida passa a não retornar nada em vez de retornar dados de outro cliente.

02A configuração mínima

Ative o RLS na tabela e crie uma política que compare o tenant da linha com uma configuração definida para a transação atual.

<schema.sql
01ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
02ALTER TABLE invoices FORCE ROW LEVEL SECURITY;
03
04CREATE POLICY tenant_isolation ON invoices
05 USING (tenant_id = current_setting('app.tenant_id')::uuid)
06 WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);

USING filtra o que uma consulta pode ler. WITH CHECK bloqueia inserts e updates que gravariam uma linha em outro tenant. FORCE aplica a política também ao dono da tabela.

03Definindo o tenant a cada requisição

A API identifica o tenant pelo usuário autenticado e define o valor no início de cada transação. O terceiro argumento de set_config deixa o valor restrito à transação, o que importa quando as conexões ficam num pool.

<TenantScope.java
01@Transactional
02public <T> T inTenant(UUID tenantId, Supplier<T> work) {
03 jdbc.queryForObject("SELECT set_config('app.tenant_id', ?, true)",
04 String.class, tenantId.toString());
05 return work.get();
06}

04Três erros que quebram o isolamento

<Conectar como superusuário ou com um papel que tem BYPASSRLS. As políticas são ignoradas sem nenhum erro. Use um papel dedicado para a aplicação.
<Definir o tenant no nível da sessão com pool de conexões. A próxima requisição na mesma conexão herda o tenant anterior.
<Esquecer tabelas novas. Adicione uma checagem nas migrações que falhe quando uma tabela com tenant_id estiver sem RLS.
O RLS é uma segunda linha de defesa. Mantenha os filtros de tenant também na aplicação e teste o isolamento no CI com dois tenants e as mesmas consultas.

05Testando no CI

<isolation_test.sql
01BEGIN;
02SELECT set_config('app.tenant_id', '00000000-0000-0000-0000-0000000000a1', true);
03SELECT count(*) FROM invoices
04 WHERE tenant_id = '00000000-0000-0000-0000-0000000000b2';
05-- expected: 0
06ROLLBACK;

Rodamos essa checagem para toda tabela com coluna de tenant em todo pull request. Leva segundos e pega o único erro que custaria um cliente.

[ FOTO ]
SOBRE O AUTOR[ PLACEHOLDER · Nome, cargo e uma linha de trajetória ]
< Todos os artigos
POR QUE CONFIAM NA GENTE
NDA antes de tudoAssinamos o seu NDA antes de ver uma linha do seu código.
O código é seu100% do código e da propriedade intelectual ficam com a sua empresa.
LGPD e GDPRDados pessoais tratados conforme a LGPD e as regras europeias.
Acesso desde o dia 1Repositório e board compartilhados desde o primeiro dia.
Demo toda semanaSoftware funcionando, apresentado ao vivo toda semana.
Uma empresa da WGB Soluções de Software · Santa Rita do Sapucaí, MG
[ PLACEHOLDER · Selo Clutch ][ PLACEHOLDER · Selo Upwork ]
CONTATO

Tem um projeto com prazo?

Mande os requisitos. Voltamos com escopo, cronograma e preço fechado.

O QUE ACONTECE DEPOIS
Dia 0Uma conversa de 30 minutos com um tech lead. Assinamos o seu NDA antes, se precisar.
Dia 1Um resumo por escrito da conversa e as nossas dúvidas.
Dias 3–7Uma proposta com escopo, prazo, time e preço fechado.