Roles & RLS In Depth
O modelo de controle de acesso do PostgreSQL baseia-se em uma única ideia que confunde pessoas de outros bancos de dados: não existe um conceito separado de "usuário" e "grupo".
Ambos são o mesmo objeto subjacente, um role, e todo o resto nesta seção, de GRANT à segurança em nível de linha, é construído sobre essa única unificação.
Resumo
- Um role é o único tipo de principal do PostgreSQL; se ele se comporta como um "usuário" ou um "grupo" depende apenas de seus atributos e de como outros roles são concedidos a ele.
- Por que é Importante: Privilégios em nível de objeto (
GRANT) e segurança em nível de linha (RLS) respondem a duas perguntas diferentes: "este role pode acessar esta tabela" e "quais linhas dessa tabela ele pode ver", e confundi-las causa lacunas de segurança reais. - Conceitos Chave: role, membership (associação), inheritance (herança), privilege (privilégio), row-level security policy (política de segurança em nível de linha),
FORCE ROW LEVEL SECURITY. - Quando Usar: Projetar uma hierarquia de roles de menor privilégio, construir isolamento de linha multi-tenant, ou auditar por que uma consulta retorna menos linhas do que o esperado.
- Limitações / Trade-offs: RLS adiciona um custo real de planejamento e execução por linha, e os proprietários de tabelas ignoram silenciosamente todas as políticas, a menos que você o force explicitamente.
- Tópicos Relacionados: padrões multi-tenant, pooling de conexões e variáveis de sessão, concessões de privilégios em tempo de migração, auditoria de segurança.
Fundamentos
Documentação mais antiga do PostgreSQL ainda se refere a CREATE USER, mas esse comando é apenas um invólucro de conveniência: ele cria um role com o atributo LOGIN definido, e nada mais.
Um role sem LOGIN se comporta como o que outros sistemas chamam de "grupo", existindo puramente para deter privilégios que outros roles podem herdar.
Esse design de objeto único significa que GRANT app_readers TO app_api e GRANT SELECT ON orders TO app_readers são o mesmo tipo de instrução, apenas concedendo coisas diferentes a um role.
Uma analogia útil é um sistema de crachás em um prédio: um role é um crachá, a associação a outro role é prender um segundo crachá em seu cordão, e INHERIT decide se o leitor de crachás verifica ambos os crachás automaticamente ou apenas o que você apresenta explicitamente.
GRANT, REVOKE e associação de roles juntos decidem quais objetos um role tem permissão para acessar - uma tabela, um esquema, uma função.
A segurança em nível de linha é uma camada separada e posterior que decide quais linhas dentro de um objeto um role tem permissão para ver, depois que ele já passou pela verificação em nível de objeto.
Mecânicas e Interações
Verificações de privilégio e verificações de RLS ocorrem em pontos diferentes e respondem a perguntas diferentes, que é a distinção mais importante em todo este modelo.
Uma verificação de privilégio em nível de objeto ocorre uma vez, quando o planejador confirma que seu role tem direitos SELECT, INSERT, UPDATE ou DELETE na tabela.
Uma política de segurança em nível de linha, uma vez ativada, é incorporada ao plano de consulta como um filtro implícito, e seu predicado é avaliado por linha em vez de uma vez por instrução.
Isso significa que RLS não é uma permissão no sentido de GRANT; é mais próximo de uma cláusula WHERE injetada automaticamente que o role não pode ver ou remover.
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id')::int);USING controla quais linhas existentes são visíveis para SELECT, UPDATE e DELETE; WITH CHECK controla separadamente quais linhas uma linha nova ou modificada tem permissão para se tornar, e omitir WITH CHECK em uma política relevante para UPDATE reutiliza silenciosamente a cláusula USING em vez disso.
O proprietário da tabela e qualquer role com o atributo BYPASSRLS ignoram todas as políticas por padrão, o que é uma escolha de design deliberada para que os roles de migração e administração não sejam acidentalmente bloqueados de suas próprias tabelas.
FORCE ROW LEVEL SECURITY existe especificamente para fechar essa lacuna quando o próprio role proprietário ainda deve estar sujeito às suas próprias políticas, o que é mais importante quando o código do aplicativo é executado como o role proprietário.
Considerações Avançadas e Aplicações
Políticas de RLS comumente se baseiam em uma variável de sessão definida uma vez por conexão, como current_setting('app.tenant_id'), que reintroduz silenciosamente a distinção de estado de sessão cliente/servidor coberta em outras partes deste cookbook.
Essa dependência é exatamente por que RLS e pooling de conexões precisam de coordenação deliberada: uma conexão agrupada reutilizada entre tenants sem redefinir essa variável de sessão pode vazar linhas de um tenant para a solicitação de outro tenant.
O desempenho é o outro lugar onde RLS merece uma análise real, porque uma coluna não indexada referenciada na cláusula USING de uma política transforma cada consulta contra essa tabela em um filtro implícito por linha sem um índice para podar.
Indexar as colunas em que suas políticas filtram, tipicamente tenant_id, não é opcional em escala; é a diferença entre RLS não custar nada extra e RLS silenciosamente dobrar a latência da consulta.
Privilégios padrão (ALTER DEFAULT PRIVILEGES) resolvem um problema relacionado, mas distinto: sem eles, cada tabela que uma migração cria precisa ter suas instruções GRANT repetidas manualmente, e é fácil enviar uma nova tabela sem nenhuma concessão, o que falha de forma segura para segurança, mas quebra o aplicativo em tempo de execução.
| Mecanismo | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
GRANT / REVOKE (privilégios de objeto) | Simples, bem compreendido, verificado uma vez por instrução | Tudo ou nada por objeto; não pode restringir a um subconjunto de linhas | Controlar quais tabelas/esquemas um role pode acessar |
| Políticas de segurança em nível de linha | Granular, aplicado mesmo contra consultas ad-hoc | Custo real por linha; invisível a menos que você saiba procurar | Isolamento de linha multi-tenant, dados com escopo de tenant |
Filtragem em nível de aplicativo (WHERE tenant_id = ? no código do app) | Sem custo do lado do banco de dados; mais simples de raciocinar localmente | Apenas tão forte quanto cada caminho de código que lembra o filtro | Ferramentas internas de baixo risco onde um filtro perdido não é catastrófico |
Conceitos Equivocados Comuns
- "O PostgreSQL tem conceitos separados de USER e ROLE."
CREATE USERé um invólucro fino em torno deCREATE ROLE ... LOGIN; por baixo, usuários e grupos são o mesmo tipo de objeto. - "Políticas de RLS são um tipo de GRANT."
GRANTdecide se um role pode acessar uma tabela; RLS decide quais linhas de uma tabela já permitida ele pode ver, e as duas verificações ocorrem em estágios diferentes. - "Ativar RLS em uma tabela a protege automaticamente do proprietário." O proprietário da tabela ignora RLS por padrão; apenas
FORCE ROW LEVEL SECURITYsujeita o proprietário às suas próprias políticas. - "Uma política com apenas USING também restringe quais linhas podem ser escritas." Sem um
WITH CHECKexplícito,UPDATEeINSERTreutilizam a cláusulaUSING, o que muitas vezes não é a restrição que você realmente pretendia para escritas. - "RLS é gratuito depois de ativado." Um predicado de política não indexado se torna um filtro implícito por linha sem um índice para podar, o que pode desacelerar significativamente todas as consultas contra essa tabela.
FAQs
Qual é a diferença real entre um "usuário" e um "role" no PostgreSQL?
Não há nenhuma no nível do objeto - CREATE USER apenas cria um role com o atributo LOGIN definido, então "usuário" e "grupo" são ambos instâncias do mesmo objeto ROLE.
Como a associação de roles se relaciona com a herança?
A associação (GRANT roleA TO roleB) torna o roleB um membro do roleA, e INHERIT (o padrão) significa que o roleB obtém automaticamente os privilégios do roleA sem precisar de SET ROLE primeiro.
O que GRANT realmente controla, em comparação com uma política de segurança em nível de linha?
GRANT controla se um role pode acessar um objeto; RLS controla quais linhas específicas dentro de uma tabela já permitida o role pode ver ou modificar.
Os privilégios de objeto e RLS são verificados no mesmo ponto da execução da consulta?
Não - privilégios de objeto são verificados uma vez por instrução durante o planejamento, enquanto o predicado de uma política RLS é incorporado ao plano e avaliado por linha.
O proprietário da tabela tem que seguir as políticas de RLS em sua própria tabela?
Não por padrão - proprietários e roles com BYPASSRLS ignoram todas as políticas, a menos que a tabela tenha FORCE ROW LEVEL SECURITY definido.
Qual é a diferença entre USING e WITH CHECK em uma política?
USING filtra quais linhas existentes são visíveis para leituras, atualizações e exclusões; WITH CHECK governa separadamente quais linhas uma escrita pode resultar, e ele usa a expressão USING como padrão se omitido.
Por que RLS comumente depende de uma variável de sessão como current_setting('app.tenant_id')?
Porque a política precisa de algum valor por conexão para comparar com cada linha, e um GUC de sessão definido uma vez no momento da conexão é a maneira padrão de passar esse contexto para a expressão da política.
O pooling de conexões pode quebrar o isolamento de tenant baseado em RLS?
Sim, se uma conexão agrupada for reutilizada entre tenants sem redefinir a variável de sessão da qual a política depende, o que pode vazar linhas de um tenant para a consulta de outro tenant.
Ativar RLS deixa as consultas mais lentas?
Pode sim, especialmente se a coluna referenciada na política não estiver indexada, já que o predicado então é executado como um filtro não indexado por linha em cada consulta contra essa tabela.
Por que privilégios padrão (ALTER DEFAULT PRIVILEGES) importam para migrações?
Sem eles, cada nova tabela que uma migração cria começa sem concessões, então um role de aplicativo pode falhar em tempo de execução em uma tabela que foi criada com sucesso, mas nunca explicitamente concedida.
Quando a filtragem em nível de aplicativo é uma alternativa razoável ao RLS?
Para ferramentas internas de baixo risco onde um WHERE tenant_id = ? perdido não é catastrófico; onde quer que um filtro perdido seja um incidente de segurança real, a garantia aplicada pelo servidor do RLS vale seu custo.
RLS é suficiente por si só para tornar uma tabela multi-tenant segura?
Apenas quando combinado com o manuseio correto de variáveis de sessão e indexação - RLS aplica a lógica da política corretamente, mas uma variável de sessão vazada ou não definida, ou um predicado não indexado, mina tanto sua segurança quanto seu desempenho.
Relacionados
- Fundamentos de Roles - criação de roles de login e de grupo
- Padrões GRANT/REVOKE - mecânicas de privilégios em nível de objeto
- Segurança em Nível de Linha - sintaxe de política e
USING/WITH CHECKem detalhe - Desempenho de RLS - indexando colunas de política para velocidade
- Bypass RLS & Superusuário - quando as políticas não se aplicam
Versões do Stack: Esta página é conceitual e não está vinculada a uma versão específica do stack; as mecânicas de role e segurança em nível de linha descritas aqui são consistentes em versões principais atuais do PostgreSQL, incluindo PostgreSQL 18.4 (linha estável 18, linha de manutenção 17).