KoderKoder.ai
PreçosEnterpriseEducaçãoPara investidores
EntrarComeçar

Produto

PreçosEnterprisePara investidores

Recursos

Fale conoscoSuporteEducaçãoBlog

Jurídico

Política de privacidadeTermos de usoSegurançaPolítica de uso aceitávelDenunciar abuso

Social

LinkedInTwitter
Koder.ai
Idioma

© 2026 Koder.ai. Todos os direitos reservados.

Início›Blog›Row-level security do PostgreSQL para SaaS: políticas que funcionam
14 de out. de 2025·8 min

Row-level security do PostgreSQL para SaaS: políticas que funcionam

A segurança ao nível de linha (RLS) do PostgreSQL para SaaS ajuda a reforçar o isolamento de clientes/organizações no banco de dados. Saiba quando usar, como escrever políticas e o que evitar.

Row-level security do PostgreSQL para SaaS: políticas que funcionam

O problema real que o RLS tenta resolver em apps SaaS

Em um app SaaS, o bug de segurança mais perigoso é o que aparece depois que você escala. Você começa com uma regra simples do tipo “usuários só podem ver dados do seu tenant”, então lança um novo endpoint rápido, adiciona uma query de relatório ou introduz um join que silenciosamente ignora a checagem.

A autorização só na aplicação falha sob pressão porque as regras acabam espalhadas. Um controller verifica tenant_id, outro verifica membership, um job em background esquece, e um caminho de “admin export” fica “temporário” por meses. Mesmo equipes cuidadosas perdem um ponto.

O row-level security (RLS) do PostgreSQL resolve um problema específico: faz com que o banco force quais linhas são visíveis para uma requisição. O modelo mental é simples: todo SELECT, UPDATE e DELETE é automaticamente filtrado por políticas, do mesmo modo que todo pedido é filtrado pelo middleware de autenticação.

O “rows” importa. RLS não protege tudo:

  • Não oculta colunas específicas por si só (use views ou privilégios de coluna).
  • Não torna funções inseguras em seguras (uma função ainda pode vazar dados se rodar com privilégios elevados).
  • Não valida regras de negócio complexas (por exemplo, “apenas proprietários podem alterar configurações de cobrança”).

Um exemplo concreto: você adiciona um endpoint que lista projetos com um join em invoices para um dashboard. Só na aplicação é fácil filtrar projects por tenant e esquecer de filtrar invoices, ou fazer um join numa chave que cruza tenants. Com RLS, ambas as tabelas podem reforçar o isolamento por tenant, de modo que a query falhe de forma segura em vez de vazar dados.

O trade-off é real. Você escreve menos código de autorização repetido e reduz os pontos que podem vazar. Mas também assume trabalho novo: precisa desenhar políticas com cuidado, testá-las cedo e aceitar que uma política pode bloquear uma query que você esperava que funcionasse.

Quando o RLS simplifica a autorização (e quando ele complica)

RLS pode parecer trabalho extra até seu app passar de um punhado de endpoints. Se você tem limites rígidos entre tenants e muitos caminhos de consulta (telas de listagem, busca, exports, ferramentas admin), colocar a regra no banco significa que você não precisa lembrar de adicionar o mesmo filtro em todo lugar.

RLS é uma boa escolha quando a regra é chata e universal: “um usuário só vê linhas do seu tenant” ou “um usuário só vê projetos dos quais é membro”. Nesses cenários, políticas reduzem erros porque todo SELECT, UPDATE e DELETE passa pelo mesmo portão, mesmo quando uma query é adicionada depois.

Também ajuda em apps com leitura intensiva onde a lógica de filtragem é consistente. Se sua API tem 15 maneiras diferentes de carregar invoices (por status, data, cliente, busca), RLS permite parar de reimplementar o filtro por tenant em cada consulta e focar na funcionalidade.

RLS complica quando as regras não são baseadas em linhas. Regras por campo como “você pode ver salário mas não bônus” ou “mascarar esta coluna a menos que você seja RH” viram SQL esquisito e exceções difíceis de manter.

Também é um ajuste ruim para relatórios pesados que genuinamente precisam de acesso amplo. Equipes costumam criar roles de bypass para “só este job”, e é aí que erros se acumulam.

Antes de se comprometer, decida se você quer que o banco seja o portão final. Se sim, planeje a disciplina: teste o comportamento do banco (não só respostas da API), trate migrations como mudanças de segurança, evite bypasses rápidos, decida como jobs em background se autenticam e mantenha políticas pequenas e repetíveis.

Se você usa ferramentas que geram backends, isso pode acelerar a entrega, mas não elimina a necessidade de roles claras, testes e um modelo de tenant simples. (Por exemplo, Koder.ai usa Go e PostgreSQL para backends gerados, e você ainda quer desenhar RLS deliberadamente em vez de “salpicar depois”.)

Noções básicas do modelo de dados que tornam políticas RLS gerenciáveis

RLS é mais fácil quando seu esquema já diz claramente quem possui o quê. Se você começa com um modelo nebuloso e tenta “consertar com políticas”, normalmente ganha queries lentas e bugs confusos.

Coloque uma chave de tenant onde ela pertence

Escolha uma chave de tenant (como org_id) e use-a consistentemente. A maioria das tabelas owned por tenant deve tê-la, mesmo que também referenciem outra tabela que a tenha. Isso evita joins dentro de políticas e mantém checagens USING simples.

Uma regra prática: se uma linha deve desaparecer quando um cliente cancela, provavelmente precisa de org_id.

Modele memberships explicitamente

Políticas RLS geralmente respondem a uma pergunta: “Este usuário é membro desta org, e o que ele pode fazer?” Isso é difícil de inferir a partir de colunas ad hoc.

Mantenha as tabelas centrais pequenas e simples:

  • users (uma linha por pessoa)
  • orgs (uma linha por tenant)
  • org_memberships (user_id, org_id, role, status)
  • opcional: project_memberships para acesso por projeto

Com isso, suas políticas podem checar membership com uma única busca indexada.

Separe dados de referência compartilhados de dados owned pelo tenant

Nem tudo precisa de org_id. Tabelas de referência como countries, categorias de produto ou tipos de plano frequentemente são compartilhadas entre tenants. Torne-as somente-leitura para a maior parte das roles e não as vincule a uma org.

Dados owned por tenant (projetos, invoices, tickets) devem evitar puxar detalhes específicos de tenant através de tabelas compartilhadas. Mantenha tabelas compartilhadas mínimas e estáveis.

FKs, cascades e indexação

Foreign keys continuam funcionando com RLS, mas deletes podem surpreender se a role que deleta não consegue “ver” linhas dependentes. Planeje cascades com cuidado e teste fluxos reais de delete.

Indexe as colunas que suas políticas filtram, especialmente org_id e chaves de membership. Uma política que parece WHERE org_id = ... não deve virar um table scan quando a tabela atingir milhões de linhas.

Como políticas RLS funcionam, sem partes assustadoras

RLS é um interruptor por tabela. Uma vez habilitado, o PostgreSQL para de confiar que seu código aplicacional lembra do filtro de tenant. Todo SELECT, UPDATE e DELETE é filtrado por políticas, e todo INSERT e UPDATE é validado por políticas.

A maior mudança mental: com RLS ligado, queries que antes retornavam dados podem começar a retornar zero linhas sem erro. Isso é o PostgreSQL fazendo controle de acesso.

O que políticas realmente fazem

Políticas são regras pequenas atreladas a uma tabela. Elas usam dois checks:

  • USING é o filtro de leitura. Se uma linha não bater com o USING, ela fica invisível para SELECT e não pode ser alvo de UPDATE ou DELETE.
  • WITH CHECK é o portão de escrita. Decide que novas ou alteradas linhas são permitidas para INSERT ou UPDATE.

Um padrão comum em SaaS: USING garante que você só veja linhas do seu tenant, e WITH CHECK garante que você não consiga inserir uma linha no tenant de outra pessoa chutando um tenant_id.

PERMISSIVE vs RESTRICTIVE, em uma frase

Quando você adiciona mais políticas depois, isso importa:

  • PERMISSIVE (padrão): uma linha é permitida se qualquer política permitir.
  • RESTRICTIVE: uma linha é permitida somente se todas as políticas restritivas permitirem (além do comportamento permissive).

Se você planeja empilhar regras como conferência de tenant + checagem de role + membership de projeto, políticas restrictive podem clarificar a intenção, mas também tornam mais fácil se trancar fora se você esquecer uma condição.

Como o Postgres sabe quem é o usuário

RLS precisa de um valor confiável “quem está chamando”. Opções comuns:

  • Uma variável de sessão definida por requisição (por exemplo, app.user_id e app.tenant_id).
  • Claims de JWT mapeadas para settings de sessão pela sua camada de API.
  • Troca de role (SET ROLE ...) por requisição, que pode funcionar mas adiciona overhead operacional.

Escolha uma abordagem e aplique-a em todos os lugares. Misturar fontes de identidade entre serviços é caminho rápido para bugs confusos.

Nomeando políticas para facilitar debug depois

Use uma convenção previsível para que dumps de schema e logs permaneçam legíveis. Por exemplo: {table}__{action}__{rule}, como projects__select__tenant_match.

Passo a passo: adicione RLS a uma tabela e prove que funciona

Aplique RLS a dados SaaS comuns
Construa um CRM baseado em tenant e aplique os mesmos padrões RLS em todas as tabelas.
Iniciar CRM

Se você é novo em RLS, comece com uma tabela e uma prova pequena. O objetivo não é cobertura perfeita. O objetivo é fazer o banco recusar acesso cross-tenant mesmo quando houver um bug na app.

Uma tabela pequena para praticar

Assuma uma tabela simples projects. Primeiro, adicione tenant_id de forma que não quebre gravações.

ALTER TABLE projects ADD COLUMN tenant_id uuid;

-- Backfill existing rows (example: everyone belongs to a default tenant)
UPDATE projects SET tenant_id = '11111111-1111-1111-1111-111111111111'::uuid
WHERE tenant_id IS NULL;

ALTER TABLE projects ALTER COLUMN tenant_id SET NOT NULL;

Em seguida, separe propriedade de acesso. Um padrão comum é: uma role que é dona das tabelas (app_owner) e outra usada pela API (app_user). A role da API não deve ser dona das tabelas, ou ela pode burlar políticas.

ALTER TABLE projects OWNER TO app_owner;
REVOKE ALL ON projects FROM PUBLIC;
GRANT SELECT, INSERT, UPDATE, DELETE ON projects TO app_user;

Agora decida como a requisição informa ao Postgres qual tenant está servindo. Uma abordagem simples é um setting com escopo de requisição. Sua app o define logo após abrir uma transação.

-- inside the same transaction as the request
SELECT set_config('app.current_tenant', '22222222-2222-2222-2222-222222222222', true);

Habilite RLS e comece pelo acesso de leitura.

ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

CREATE POLICY projects_tenant_select
ON projects
FOR SELECT
TO app_user
USING (tenant_id = current_setting('app.current_tenant')::uuid);

Prove que funciona tentando dois tenants diferentes e verificando que a contagem de linhas muda.

Adicione regras de escrita (WITH CHECK)

Políticas de leitura não protegem escritas. Adicione WITH CHECK para que inserts e updates não contrabandeiem linhas para o tenant errado.

CREATE POLICY projects_tenant_write
ON projects
FOR INSERT, UPDATE
TO app_user
WITH CHECK (tenant_id = current_setting('app.current_tenant')::uuid);

Uma forma rápida de verificar o comportamento (incluindo falhas) é manter um pequeno script SQL que você possa repetir após cada migration:

  • BEGIN; SET LOCAL ROLE app_user;
  • SELECT set_config('app.current_tenant', '\u003ctenant A\u003e', true); SELECT count(*) FROM projects;
  • INSERT INTO projects(id, tenant_id, name) VALUES (gen_random_uuid(), '\u003ctenant B\u003e', 'bad'); (deve falhar)
  • UPDATE projects SET tenant_id = '\u003ctenant B\u003e' WHERE ...; (deve falhar)
  • ROLLBACK;

Se você consegue rodar esse script e obter resultados consistentes sempre, tem uma baseline confiável antes de expandir RLS para outras tabelas.

Padrões de política que você reutilizará na maioria dos apps SaaS

A maioria das equipes adota RLS depois de cansar de repetir as mesmas checagens de autorização em cada query. A boa notícia é que os formatos de política que você precisa costumam ser consistentes.

Linhas de owner vs linhas de membership

Algumas tabelas são naturalmente owned por um usuário (notes, API tokens). Outras pertencem a um tenant onde o acesso depende de membership. Trate esses padrões de forma diferente.

Para dados de owner, políticas frequentemente checam created_by = app_user_id(). Para dados de tenant, políticas checam se o usuário tem uma linha de membership para a org.

Uma forma prática de manter políticas legíveis é centralizar identidade em helpers SQL pequenos e reusá-los:

-- Example helpers
create function app_user_id() returns uuid
language sql stable as $$
  select current_setting('app.user_id', true)::uuid
$$;

create function app_is_admin() returns boolean
language sql stable as $$
  select current_setting('app.is_admin', true) = 'true'
$$;

Separe regras de leitura das de escrita

Leituras costumam ser mais amplas que escritas. Por exemplo, qualquer membro da org pode SELECT projetos, mas só editores podem UPDATE, e apenas proprietários podem DELETE.

Mantenha explícito: uma política para SELECT (membership), uma para INSERT/UPDATE com WITH CHECK (role) e uma para DELETE (frequentemente mais rígida que update).

Override de admin sem desabilitar RLS

Evite “desligar o RLS para admins”. Em vez disso, adicione uma saída dentro das políticas, como app_is_admin(), para não conceder acidentalmente acesso total a uma role de serviço compartilhada.

Soft deletes e flags de status

Se você usa deleted_at ou status, inclua isso na política de SELECT (deleted_at is null). Caso contrário, alguém pode “ressuscitar” linhas alterando flags que a app assumia como finais.

UPSERT: mantenha WITH CHECK amigável

INSERT ... ON CONFLICT DO UPDATE deve satisfazer WITH CHECK para a linha após a escrita. Se sua política exige created_by = app_user_id(), garanta que o upsert defina created_by no insert e não o sobrescreva no update.

Se você gera código backend, esses padrões valem a pena virar templates internos para que novas tabelas iniciem com defaults seguros em vez de um papel em branco.

Armadilhas comuns do RLS que tornam o debug doloroso

RLS é ótimo até um pequeno detalhe fazer parecer que o PostgreSQL está “aleatoriamente” escondendo ou mostrando dados. Os erros abaixo são os que mais consomem tempo.

Armadilhas que causam vazamentos silenciosos entre tenants

A primeira armadilha é esquecer WITH CHECK em insert e update. USING controla o que você pode ver, não o que pode criar. Sem WITH CHECK, um bug pode escrever uma linha no tenant errado, e você pode não perceber porque esse mesmo usuário não consegue ler a linha.

Outro vazamento comum é o “join vazante”. Você filtra projects corretamente, depois faz join em invoices, notes ou files que não estão protegidos da mesma forma. A correção é estrita mas direta: toda tabela que possa revelar dados por tenant precisa de sua própria política, e views não devem depender de apenas uma tabela estar segura.

Padrões de falha comuns aparecem cedo:

  • Existe política de leitura, mas falta WITH CHECK para escrita.
  • Uma condição de política usa join para outra tabela que não está protegida.
  • O acesso é aplicado em uma view, mas a tabela subjacente ainda está aberta.
  • Você confia que “a app sempre define tenant_id”, e um job em background esquece.
  • Você testa com uma role superuser, então nunca vê o comportamento real.

Armadilhas que tornam o comportamento confuso

Políticas que referenciam a mesma tabela (diretamente ou via view) podem criar surpresas de recursão. Uma política pode checar membership consultando uma view que lê a tabela protegida de novo, levando a erros, queries lentas ou uma política que nunca casa.

A configuração de roles é outra fonte de confusão. Donos de tabela e roles elevadas podem burlar RLS, então seus testes passam enquanto usuários reais falham (ou o contrário). Sempre teste com a role de baixo privilégio que sua app usa.

Tenha cautela com funções SECURITY DEFINER. Elas rodam com privilégios do dono da função, então um helper como current_tenant_id() pode ser ok, mas uma função “conveniente” que lê dados pode acidentalmente ler entre tenants, a menos que você a desenhe para respeitar RLS.

Também defina um search_path seguro dentro de funções security definer. Caso contrário, a função pode encontrar um objeto com o mesmo nome em outro schema, e sua lógica de política pode apontar para a coisa errada dependendo do estado da sessão.

Debugando RLS: formas práticas de ver o que está acontecendo

Implemente fundações multi-tenant mais rápido
Transforme seu modelo de tenant em tabelas, roles e endpoints reais sem começar do zero.
Experimente Koder.ai

Bugs de RLS geralmente são falta de contexto, não “SQL ruim”. Uma política pode estar correta no papel e ainda falhar porque a role de sessão é diferente do que você pensa, ou porque a requisição nunca definiu os valores de tenant e usuário que a política espera.

Uma maneira confiável de reproduzir um relatório de produção é espelhar a mesma configuração de sessão localmente e rodar a query exata. Isso normalmente significa:

  • SET ROLE app_user; (ou a role real da API)
  • SELECT set_config('app.tenant_id', 't_123', true); e SELECT set_config('app.user_id', 'u_456', true);
  • Rode o mesmo SQL que sua app rodou (incluindo parâmetros)
  • Confirme o que o Postgres vê: SELECT current_user, current_setting('app.tenant_id', true), current_setting('app.user_id', true);

Quando você não tem certeza de qual política está sendo aplicada, cheque o catálogo em vez de adivinhar. pg_policies mostra cada política, o comando e as expressões USING e WITH CHECK. Combine isso com pg_class para confirmar que o RLS está habilitado na tabela e não está sendo contornado.

Problemas de performance podem parecer problemas de autenticação. Uma política que faz join em membership ou chama uma função pode estar correta mas lenta quando a tabela cresce. Use EXPLAIN (ANALYZE, BUFFERS) na query reproduzida e procure por scans sequenciais, nested loops inesperados ou filtros aplicados tarde. Índices faltantes em (tenant_id, user_id) e nas tabelas de membership são causas comuns.

Também ajuda logar três valores por requisição na camada da app: o tenant ID, o user ID e a role do banco usada. Quando esses não batem com o que você acha que definiu, o RLS se comportará “errado” porque as entradas estão erradas.

Para testes, mantenha alguns tenants seedados e deixe falhas explícitas. Uma pequena suíte costuma incluir: “Tenant A não lê Tenant B”, “usuário sem membership não vê o projeto”, “owner pode atualizar, viewer não”, “insert é bloqueado a menos que tenant_id bata com o contexto”, e “override admin só aplica onde planejado”.

Checklist rápido antes de liberar RLS em produção

Trate RLS como cinto de segurança, não como um toggle de recurso. Pequenos deslizes viram “todo mundo vê tudo” ou “tudo retorna zero linhas”.

Modelo de dados e formato de política

Garanta que o design das tabelas e as regras de política casem com seu modelo de tenant.

  • Toda tabela owned por tenant deve ter uma chave clara de tenant (geralmente tenant_id). Se não tiver, registre o porquê (por exemplo, tabelas de referência globais).
  • Habilite RLS em todas as tabelas owned por tenant, não apenas nas “principais”. Se alguns caminhos nunca devem contornar, considere FORCE ROW LEVEL SECURITY nessas tabelas.
  • Separe regras de leitura e escrita. Leituras usam USING. Escritas precisam de WITH CHECK para que inserts e updates não movam uma linha para outro tenant.
  • Mantenha predicados de política amigáveis a índices. Se políticas filtram por tenant_id ou fazem joins por membership, adicione os índices correspondentes.

Um cenário simples de sanidade: um usuário do tenant A pode ler suas próprias invoices, pode inserir uma invoice apenas para o tenant A e não pode atualizar uma invoice para mudar tenant_id.

Roles, performance e testes

RLS é tão forte quanto as roles que sua app usa.

  • Confirme que a app nunca conecta como superuser, dono da tabela ou qualquer role com bypassrls.
  • Rode algumas queries reais com volume de dados parecido com produção e verifique planos de execução.
  • Adicione testes automáticos negativos que provem que acesso cross-tenant falha.

Exemplo: app multi-tenant de projetos com acesso baseado em membership

Previna joins leaky desde cedo
Crie uma tela de relatórios e valide que joins permaneçam seguros por tenant com RLS.
Criar Painel

Imagine um app B2B onde empresas (orgs) têm projetos, e projetos têm tasks. Usuários podem pertencer a múltiplas orgs, e um usuário pode ser membro de alguns projetos e não de outros. Esse é um bom caso para RLS porque o banco pode reforçar isolamento por tenant mesmo se um endpoint da API esquecer um filtro.

Um modelo simples é: orgs, users, org_memberships (org_id, user_id, role), projects (id, org_id), project_memberships (project_id, user_id), tasks (id, project_id, org_id, ...). Esse org_id em tasks é intencional. Mantém as políticas simples e reduz surpresas durante joins.

Um vazamento clássico acontece quando tasks só têm project_id e sua política checa acesso via join em projects. Um erro (uma política permissiva em projects, um join que remove uma condição ou uma view que muda contexto) pode expor tasks de outra org.

Um caminho de migração mais seguro evita quebrar tráfego em produção:

  • Lance mudanças de schema primeiro (adicione org_id a tasks, adicione tabelas de membership).
  • Preencha tasks.org_id a partir de projects.org_id, depois adicione NOT NULL.
  • Adicione políticas mas mantenha RLS desabilitado enquanto testa em staging.
  • Habilite RLS, então force-o, e só então remova filtros antigos na aplicação.

Suporte é geralmente melhor tratado com uma role estreita de break-glass, não desabilitando RLS. Mantenha-a separada de contas normais de suporte e registre explicitamente quando for usada.

Documente as regras para que políticas não divirjam: quais variáveis de sessão devem ser definidas (user_id, org_id), quais tabelas devem carregar org_id, o que “membro” significa e alguns exemplos SQL que devem retornar 0 linhas quando executados como a org errada.

Próximos passos: implantar RLS com segurança e mantê-lo sustentável

RLS é mais fácil de conviver quando você o trata como uma mudança de produto. Faça rollout em pequenos pedaços, prove comportamento com testes e mantenha um registro claro do porquê de cada política existir.

Um plano de rollout que costuma funcionar:

  • Comece com uma tabela com propriedade clara de tenant (por exemplo, projects) e bloqueie-a.
  • Adicione testes cobrindo leituras e escritas permitidas e bloqueadas para algumas roles (owner, member, outsider).
  • Expanda em lotes (uma área de funcionalidade por vez) que você consiga debugar numa única sessão.
  • Monitore erros de permissão durante o rollout e faça deploy em janela de baixo risco.

Depois que a primeira tabela estiver estável, trate mudanças de política como deliberadas. Adicione uma etapa de revisão de políticas nas migrations e inclua uma nota curta sobre a intenção (quem deve acessar o quê e por quê) junto com a atualização dos testes. Isso evita “só adiciona outro OR” que vira um buraco com o tempo.

Se você está se movendo rápido, ferramentas como Koder.ai (koder.ai) podem ajudar a gerar um ponto de partida Go + PostgreSQL via chat, e então você vai acrescentar políticas RLS e testes com a mesma disciplina de um backend feito à mão.

Por fim, mantenha redes de segurança durante o rollout. Faça snapshots antes de migrações de política, pratique rollback até ficar trivial e mantenha um pequeno caminho de break-glass para suporte que não desabilite RLS em todo o sistema.

Perguntas frequentes

Que problema de segurança o RLS realmente resolve em um app SaaS?

RLS faz o PostgreSQL impor quais linhas são visíveis ou editáveis por uma requisição, de modo que o isolamento por tenant não dependa de cada endpoint lembrar o WHERE tenant_id = .... O principal benefício é reduzir bugs do tipo “uma verificação esquecida” quando a aplicação cresce e as consultas se multiplicam.

Quando o RLS vale a complexidade extra?

Compensa quando as regras de acesso são consistentes e baseadas em linhas — por exemplo, isolamento por tenant ou acesso baseado em membership — e quando você tem muitos caminhos de consulta (buscas, exports, telas administrativas, jobs). Normalmente não vale a pena se a maior parte das regras for por campo, cheia de exceções, ou dominada por relatórios amplos que precisam ler entre tenants.

O que o RLS NÃO me protege?

Use RLS para visibilidade de linhas e controle básico de escrita; o resto continua com outras ferramentas. Privacidade de colunas costuma exigir views e privilégios de coluna, e regras de negócio complexas (por exemplo, propriedade de cobrança ou fluxos de aprovação) ainda pertencem à lógica da aplicação ou a constraints cuidadosamente desenhadas no banco.

Qual a forma mais segura de começar a usar RLS se eu nunca usei?

Crie uma role de baixo privilégio para a API (não a dona das tabelas), habilite RLS, depois adicione uma política de SELECT e uma política de INSERT/UPDATE com WITH CHECK. Use um valor de sessão por requisição (por exemplo, app.current_tenant) e verifique que alterná-lo muda quais linhas você vê e pode gravar.

Como minha aplicação deve informar ao Postgres qual tenant e usuário estão fazendo a requisição?

Um padrão comum é uma variável de sessão por requisição, definida no começo da transação, como app.tenant_id e app.user_id. O importante é consistência: todo caminho de código (requisições web, jobs, scripts) deve definir os mesmos valores que as políticas esperam, caso contrário você terá comportamentos “zero rows” confusos.

Qual a diferença entre USING e WITH CHECK em uma política RLS?

USING controla o filtro de leitura. Se uma linha não bater com o USING, ela fica invisível para e não pode ser alvo de ou . é o portão de escrita: decide que novas ou alteradas linhas são permitidas em ou . Em resumo: filtra o que você vê; valida o que pode ser escrito.

Por que insistem tanto em “não esqueça o WITH CHECK”?

Porque, se você só adicionar USING, um endpoint com bug ainda pode inserir ou atualizar linhas para outro tenant, e você talvez não note porque o mesmo usuário não conseguirá ler a linha errada. Sempre combine regras de leitura por tenant com um WITH CHECK correspondente para escritas, assim dados incorretos não podem ser criados.

Como devo estruturar meu esquema para manter políticas RLS simples e rápidas?

Evite joins dentro de políticas colocando a chave de tenant (por exemplo, org_id) diretamente nas tabelas owned pelo tenant, mesmo que elas referenciem outra tabela que também tenha esse campo. Use tabelas explícitas de membership (org_memberships, e opcionalmente project_memberships) para que a política faça uma única busca indexada em vez de inferências complicadas.

Como depuro “o RLS está escondendo meus dados” sem chutar?

Reproduza o mesmo contexto de sessão que sua aplicação usa: defina a mesma role e as mesmas configurações de sessão, e rode a consulta exata. Depois, confirme que o RLS está habilitado e inspecione pg_policies para ver quais expressões USING e WITH CHECK existem — RLS costuma falhar por falta de contexto de identidade, não por “SQL ruim”.

Se eu gerar meu backend (por exemplo com Koder.ai), ainda preciso desenhar RLS com cuidado?

Sim — trate o código gerado como ponto de partida, não como sistema de segurança pronto. Mesmo se você usar Koder.ai para gerar um backend Go + PostgreSQL, você precisa definir seu modelo de tenant, garantir identidade de sessão consistente e adicionar políticas e testes deliberadamente para que novas tabelas não sejam liberadas sem as proteções certas.

Sumário
O problema real que o RLS tenta resolver em apps SaaSQuando o RLS simplifica a autorização (e quando ele complica)Noções básicas do modelo de dados que tornam políticas RLS gerenciáveisComo políticas RLS funcionam, sem partes assustadorasPasso a passo: adicione RLS a uma tabela e prove que funcionaPadrões de política que você reutilizará na maioria dos apps SaaSArmadilhas comuns do RLS que tornam o debug dolorosoDebugando RLS: formas práticas de ver o que está acontecendoChecklist rápido antes de liberar RLS em produçãoExemplo: app multi-tenant de projetos com acesso baseado em membershipPróximos passos: implantar RLS com segurança e mantê-lo sustentávelPerguntas frequentes
Compartilhar
Koder.ai
Crie seu próprio app com Koder hoje!

A melhor maneira de entender o poder do Koder é experimentar você mesmo.

Comece GrátisAgendar Demo
SELECT
UPDATE
DELETE
WITH CHECK
INSERT
UPDATE
USING
WITH CHECK