O filtro de cliente pode desaparecer da consulta

Um agente recebe a tarefa de resumir pedidos de uma conta e produz uma consulta sem a condição de cliente. Se a credencial enxerga toda a tabela, o erro já ocorreu antes de o modelo redigir a resposta.

Dois compartimentos com peças cerâmicas separados por parede opaca; um braço alcança apenas o compartimento próximo.
Ilustração conceitual. O escopo autorizado deve delimitar recuperação antes da seleção; relevância não permite cruzar cliente ou finalidade.

A segurança por linha, ou Row-Level Security (RLS), permite que o PostgreSQL aplique políticas sobre quais registros um papel pode consultar ou modificar. Ela acrescenta uma barreira no banco, inclusive quando a consulta não contém o filtro esperado.

Este guia trata da validação dessa barreira. O isolamento entre clientes cobre também memória, arquivos, busca, cache e ferramentas. RLS protege um trecho desse caminho; uma resposta previamente armazenada em cache pode atravessar contas mesmo com a política do banco correta.

Defina a identidade antes da política

O banco precisa receber uma identidade cuja autoridade esteja delimitada. Se todas as chamadas usam uma conta administrativa, uma política bem escrita pode nem ser aplicada.

Antes de conectar o agente, identifique:

  • o papel efetivamente usado pela conexão;
  • o proprietário das tabelas;
  • atributos de superusuário e BYPASSRLS;
  • vínculos com outros papéis e caminhos de SET ROLE;
  • privilégios sobre tabela, coluna e esquema;
  • views e funções que a ferramenta pode chamar;
  • a origem autenticada do cliente da solicitação.

A escolha entre banco direto, API e ferramenta parametrizada continua dependendo da operação. O guia de acesso ao banco por agentes ajuda a decidir esse caminho antes de criar políticas.

O que USING e WITH CHECK controlam

USING define quais linhas existentes ficam disponíveis ao comando abrangido. WITH CHECK define quais valores de uma linha nova ou alterada são permitidos.

Essa diferença importa quando uma operação tenta mover um registro de uma conta para outra. Encontrar uma linha autorizada para atualização não deveria permitir trocar seu proprietário empresarial.

As políticas trabalham em conjunto com os privilégios SQL. Uma policy não concede, sozinha, SELECT ou UPDATE. O papel também precisa do privilégio correspondente. Quando RLS está habilitada e não existe política aplicável, os acessos sujeitos a RLS seguem negação por padrão.

Laboratório sintético em banco descartável

Na preparação editorial original, foram executados os controles abaixo em PostgreSQL 17.11, num contêiner local descartável, sem rede externa e sem dados empresariais. A documentação atual consultada em 22 de outubro de 2026 corresponde ao PostgreSQL 18. O resultado observado pertence à versão 17.11 testada, sem alegação de homologação da sua aplicação.

O desenho usa um papel por conta para tornar a identidade visível. Não é uma recomendação automática de criar um papel para cada cliente de um SaaS. A escolha depende de volume, provisionamento, pool de conexões e modelo de autorização.

Execute somente num banco de laboratório novo, sob identidade administrativa local autorizada:

CREATE ROLE dono_lab NOLOGIN;
CREATE ROLE conta_a NOLOGIN NOSUPERUSER NOBYPASSRLS;
CREATE ROLE conta_b NOLOGIN NOSUPERUSER NOBYPASSRLS;
CREATE ROLE sem_conta NOLOGIN NOSUPERUSER NOBYPASSRLS;

CREATE SCHEMA lab AUTHORIZATION dono_lab;
SET ROLE dono_lab;

CREATE TABLE lab.pedidos (
  id integer PRIMARY KEY,
  tenant text NOT NULL,
  detalhe text NOT NULL
);
INSERT INTO lab.pedidos VALUES
  (1, 'conta_a', 'pedido A'),
  (2, 'conta_b', 'pedido B');

ALTER TABLE lab.pedidos ENABLE ROW LEVEL SECURITY;
CREATE POLICY escopo_conta ON lab.pedidos
  TO conta_a, conta_b
  USING (tenant = current_user)
  WITH CHECK (tenant = current_user);

GRANT USAGE ON SCHEMA lab TO conta_a, conta_b, sem_conta;
GRANT SELECT, INSERT, UPDATE, DELETE ON lab.pedidos
  TO conta_a, conta_b;
GRANT SELECT ON lab.pedidos TO sem_conta;
RESET ROLE;

Os papéis são NOLOGIN: o administrador do laboratório assume cada um com SET ROLE para testar os privilégios efetivos. Não entregue ao agente a conexão administrativa usada para montar o exercício. Também não conceda a uma aplicação compartilhada liberdade para assumir qualquer conta.

O exemplo concede CRUD para exercitar a barreira de escrita. Uma ferramenta exclusivamente analítica deve receber apenas os privilégios de leitura necessários.

Teste sem filtro e com identificador de outra conta

Na sessão administrativa do laboratório:

SET ROLE conta_a;
SELECT count(*) FROM lab.pedidos;
SELECT count(*) FROM lab.pedidos WHERE id = 2;
RESET ROLE;

SET ROLE conta_b;
SELECT count(*) FROM lab.pedidos;
RESET ROLE;

SET ROLE sem_conta;
SELECT count(*) FROM lab.pedidos;
RESET ROLE;

Observamos uma linha para conta_a, zero ao procurar o ID 2, uma linha para conta_b e zero para sem_conta. A consulta sem filtro permaneceu delimitada pela policy. O papel sem política aplicável possuía SELECT, mas não enxergou linhas.

Zero linhas não prova que o pedido inexiste. Pode significar ausência de autorização. A ferramenta deve evitar transformar um resultado filtrado numa declaração global sobre o cadastro.

Tente escrever e trocar o cliente

Execute cada tentativa separadamente, voltando ao papel administrativo entre os casos:

SET ROLE conta_a;
INSERT INTO lab.pedidos VALUES (3, 'conta_b', 'invasao');

O banco recusou a inserção por violação da política de segurança por linha. A tentativa seguinte também foi recusada:

UPDATE lab.pedidos SET tenant = 'conta_b' WHERE id = 1;

Já uma atualização com WHERE id = 2, executada por conta_a, não alcançou nenhuma linha. A aplicação precisa distinguir operação sem linha afetada de alteração confirmada.

Se usar uma transação explícita para provocar erros, faça rollback antes do próximo teste. Uma transação abortada não serve como evidência de que o caso seguinte foi avaliado corretamente.

RLS: papel efetivo muda a barreira que o laboratório observa

LABORATÓRIO SINTÉTICO — resultados relatados na preparação editorial original
Identidade / operação | Observação | Controle relevante
conta_a / SELECT sem filtro | 1 linha | USING limita linhas existentes.
conta_a / SELECT com id = 2 | 0 linhas | ausência de visibilidade não prova inexistência global.
conta_b / SELECT sem filtro | 1 linha | testemunha positiva da outra conta.
sem_conta / SELECT | 0 linhas | tem SELECT, mas nenhuma policy aplicável.
dono_lab / SELECT antes de FORCE | 2 linhas | proprietário normalmente contorna RLS.
dono_lab / SELECT após FORCE | 0 linhas | sujeito à RLS, sem policy aplicável.
superusuário / SELECT após FORCE | 2 linhas | FORCE não remove seu bypass.
conta_a / inserir conta_b ou trocar tenant para conta_b | recusado | WITH CHECK barra valores não autorizados.
conta_a / UPDATE WHERE id = 2 | nenhuma linha alcançada | USING; não é alteração confirmada.
conta da aplicação / TRUNCATE | negado no laboratório | ACL, não RLS; comando de tabela inteira não é protegido pela policy.

Ficha de adaptação: papel real [ ]; privilégios SQL [ ]; proprietário/BYPASSRLS [ ]; policies [ ]; versão [ ]. Testar identidade real, views, funções e reutilização de conexão separadamente; não entregar conexão administrativa ao agente.
Recomposição explicativa original dos resultados históricos do laboratório sintético em PostgreSQL 17.11; nenhuma nova execução. Não valida PostgreSQL 18, pool, API ou cache.

Testar como proprietário pode esconder a falha

No laboratório, dono_lab viu as duas linhas mesmo com RLS habilitada. Esse é o comportamento normal do proprietário da tabela.

Depois de aplicar, como administrador:

ALTER TABLE lab.pedidos FORCE ROW LEVEL SECURITY;

O proprietário passou a ver zero linhas, pois não havia política aplicável a ele. O superusuário continuou vendo as duas.

FORCE ROW LEVEL SECURITY sujeita o proprietário às políticas, mas não remove o bypass de superusuários e papéis com BYPASSRLS. Por isso, testar somente no console administrativo pode produzir uma conclusão errada sobre a identidade usada pelo agente.

RLS não cobre toda operação SQL

Comandos de tabela inteira, como TRUNCATE, não estão sujeitos à segurança por linha. No teste da preparação editorial original, TRUNCATE foi negado ao papel da conta por falta de privilégio SQL. A negação veio da ACL, não da policy.

A documentação também alerta que verificações de integridade referencial, incluindo restrições únicas e chaves estrangeiras, podem criar canais indiretos de informação. Um erro de unicidade pode revelar algo sobre um valor que o solicitante não consegue consultar.

Outros caminhos exigem análise própria:

  • views com contexto de segurança diferente do esperado;
  • funções SECURITY DEFINER com privilégios do proprietário;
  • políticas permissivas combinadas por OR que ampliam acesso;
  • tabelas novas sem RLS;
  • permissões herdadas e concessões a PUBLIC;
  • exportações, logs e cache fora do banco;
  • políticas que consultam tabelas de autorização sob concorrência.

Não copie uma função privilegiada como atalho para resolver negação de acesso. Isso pode reabrir exatamente o caminho que a policy tentava fechar.

Cuide do contexto em pools de conexão

Uma aplicação pode usar uma identidade técnica compartilhada e transmitir o escopo por contexto de sessão ou transação. Esse desenho exige uma fronteira de confiança explícita.

Se o mesmo usuário de banco pode executar SQL arbitrário e alterar livremente a variável usada pela policy, essa variável não constitui uma identidade confiável. O modelo não pode receber autoridade para escolher o valor que autoriza a própria consulta.

O adaptador deve estabelecer o escopo depois de autenticar e autorizar o solicitante, restringir operações e impedir que a próxima solicitação herde contexto anterior. Teste troca de conta, erro, rollback e reutilização da mesma conexão. O laboratório original por papéis não valida esse comportamento de pool.

Critérios antes de conectar à operação

O responsável técnico deve reproduzir os testes com a identidade real da aplicação e registrar versão, privilégios, policies e caminhos de acesso. Inclua leitura autorizada como testemunha positiva; bloquear tudo também interrompe o trabalho legítimo.

A liberação depende de evidência de que:

  1. a conta correta consegue executar a tarefa permitida;
  2. a outra conta não consegue ler nem alterar o registro;
  3. ausência de escopo não abre acesso padrão;
  4. papéis privilegiados ficam fora do caminho normal;
  5. views, funções e pools preservam a mesma fronteira;
  6. mudanças de schema e de policy entram na regressão.

Use o ambiente de teste para agentes e o catálogo de ferramentas para preservar essas evidências por versão. O dono do processo decide quais dados são necessários; a equipe técnica comprova que a ferramenta não alcança os demais.

Fontes oficiais verificadas em 22 de outubro de 2026

O laboratório valida comportamentos delimitados de PostgreSQL 17.11. Na preparação editorial original, não foram executadas integrações com agente, API empresarial, Supabase, pool de conexões ou implantação de produção para este artigo.