Supabase RLS explicado para quem usa Lovable e Bolt
Supabase RLS explicado sem jargão: o que é Row Level Security, o erro mais comum em apps do Lovable e Bolt, exemplos de policies corretas e como testar.
RLS (Row Level Security) é a regra que diz ao banco de dados quais linhas de uma tabela cada pessoa pode ver e alterar. No Supabase, que é o backend por trás de muitos apps feitos com Lovable e Bolt, ele é a principal proteção dos seus dados, porque o navegador do usuário conversa direto com o banco. O erro mais comum é simples: tabela com RLS desligado, ou com uma policy true que libera tudo. Nos dois casos, qualquer pessoa com a chave pública do projeto (que está no código do site) consegue ler a tabela inteira.
A seguir: como o RLS funciona, os erros mais frequentes, policies corretas para casos reais e como testar sem ser especialista.
Por que o RLS é tão importante no Supabase
Num app tradicional, existe um servidor (o back-end) entre o usuário e o banco. Ele recebe o pedido, confere quem é a pessoa e só então busca os dados.
No Supabase, você pode pular esse servidor. O front-end usa a biblioteca supabase-js e uma chave pública (a publishable, antes chamada anon) para consultar o banco direto. Essa chave fica visível no código do site, e isso é esperado. O que impede o caos é o RLS: o próprio Postgres confere, a cada consulta, se aquela linha pode ser entregue para aquele usuário.
Pense assim: a chave pública é a porta do prédio, aberta para todo mundo. O RLS é a fechadura de cada apartamento.
Os conceitos em 2 minutos
- RLS ligado ou desligado: é uma configuração por tabela. Desligado, a tabela não tem filtro nenhum.
- Policy: uma regra escrita em SQL. Ex.: "usuário logado pode ler as linhas em que
user_idé igual ao ID dele". - Operação: cada policy vale para
select(ler),insert(criar),update(editar),delete(apagar) ouall. - Papel (role):
anoné quem não está logado;authenticatedé quem está logado. using: filtra quais linhas existentes a pessoa enxerga ou pode mexer.with check: valida a linha que está sendo gravada (noinserte noupdate).auth.uid(): função que devolve o ID do usuário logado.
Duas regras de ouro do Postgres:
- RLS ligado e nenhuma policy = ninguém acessa pela API com a chave pública. É seguro, mas o app para de funcionar até você criar as policies.
- Policies se somam com "OU". Se uma policy diz "só o dono" e outra diz
true, vale a mais permissiva. Uma única policy frouxa anula as outras.
Os erros mais comuns
Erro 1: tabela sem RLS
Pelo painel do Supabase, tabelas novas costumam vir com RLS ligado. Mas tabelas criadas por SQL, por migrações ou por código gerado por IA podem nascer sem ele, porque no Postgres o padrão é desligado. O Security Advisor do Supabase alerta sobre isso, e o Lovable usa esse alerta na verificação antes de publicar. Não ignore o aviso.
alter table public.perfis enable row level security;
Erro 2: a policy "true"
Quando o app dá erro de permissão, a IA (ou a pressa) às vezes "resolve" assim:
-- NÃO faça isso em tabela com dados privados
create policy "liberar tudo" on public.perfis
for all using (true) with check (true);
O erro some, o app funciona, e a tabela fica aberta para qualquer pessoa ler, editar e apagar. É, na prática, o mesmo que não ter RLS.
Erro 3: "qualquer usuário logado" achando que é proteção
-- Parece seguro, mas não é
create policy "logados leem" on public.pedidos
for select to authenticated using (true);
Criar conta costuma ser grátis e instantâneo. Então "qualquer logado" é quase o mesmo que "qualquer pessoa". O usuário A continua lendo os pedidos do usuário B.
Erro 4: esquecer o with check
Sem with check no insert e no update, o usuário pode gravar uma linha com o user_id de outra pessoa, ou transferir um registro para outra conta.
Erro 5: confiar em user_metadata
O user_metadata do Supabase Auth pode ser alterado pelo próprio usuário. Uma policy que libera acesso se user_metadata.role = 'admin' deixa qualquer um virar admin. Guarde papéis em app_metadata (que só o servidor altera) ou numa tabela própria.
Erro 6: views e a chave secret
- Views ignoram o RLS por padrão, porque normalmente são criadas pelo usuário
postgres. No Postgres 15 ou superior, crie comwith (security_invoker = true). - A chave secret (antiga service_role) ignora o RLS quando usada sem token de usuário. Se ela estiver no front-end, nenhuma policy protege nada.
Exemplos de policies corretas
Cada usuário vê e edita só o que é dele
O caso mais comum: perfis, pedidos, anotações, tarefas.
alter table public.tarefas enable row level security;
create policy "ler proprias tarefas" on public.tarefas
for select to authenticated
using ( (select auth.uid()) = user_id );
create policy "criar tarefa propria" on public.tarefas
for insert to authenticated
with check ( (select auth.uid()) = user_id );
create policy "editar propria tarefa" on public.tarefas
for update to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );
create policy "apagar propria tarefa" on public.tarefas
for delete to authenticated
using ( (select auth.uid()) = user_id );
Dica: escrever
(select auth.uid())em vez deauth.uid()é uma recomendação da própria documentação do Supabase: o Postgres calcula o valor uma vez por consulta, em vez de uma vez por linha. Em tabelas grandes, faz diferença. Crie também um índice emuser_id.
Repare que o update precisa de uma policy de select também: o usuário tem que conseguir "ver" a linha para editá-la.
Leitura pública, escrita só do dono
Para coisas realmente públicas, como posts publicados ou um catálogo:
create policy "todos leem posts publicados" on public.posts
for select to anon, authenticated
using ( publicado = true );
create policy "autor gerencia seus posts" on public.posts
for all to authenticated
using ( (select auth.uid()) = autor_id )
with check ( (select auth.uid()) = autor_id );
Aqui o true não aparece solto: a leitura pública vale só para linhas com publicado = true.
Dados compartilhados por equipe ou empresa
Apps B2B costumam ter organizações com vários membros. Uma forma segura é criar uma função auxiliar fora do schema público:
create schema if not exists private;
create or replace function private.eh_membro(org uuid)
returns boolean
language sql
security definer
set search_path = ''
stable
as $$
select exists (
select 1 from public.membros m
where m.org_id = org
and m.user_id = (select auth.uid())
);
$$;
create policy "membros leem projetos da org" on public.projetos
for select to authenticated
using ( private.eh_membro(org_id) );
A função com security definer evita um problema clássico: a policy de projetos consultar membros, que tem sua própria policy, gerando lentidão ou recursão. Colocá-la num schema que não é exposto pela API impede que alguém a chame diretamente.
Como testar o seu RLS
Não confie só em "o app funciona". Funcionar não significa estar protegido.
1. Rode o Security Advisor
No painel do Supabase, abra o Security Advisor. Ele lista tabelas sem RLS e outros riscos comuns. Resolva todos os itens críticos.
2. Teste com dois usuários
- Crie os usuários A e B.
- Como A, crie registros.
- Como B, tente ler, editar e apagar os registros de A.
- Deslogado, tente ler as tabelas.
3. Simule no SQL Editor
Dá para fingir ser um usuário específico dentro de uma transação:
begin;
set local role authenticated;
set local request.jwt.claims = '{"sub": "ID-DO-USUARIO-B", "role": "authenticated"}';
select * from public.tarefas; -- deve trazer só as tarefas de B
rollback;
Para simular um visitante deslogado, use set local role anon;.
4. Teste como um estranho faria
Com a URL do projeto e a chave pública (as mesmas que estão no código do site):
curl "https://SEU-PROJETO.supabase.co/rest/v1/tarefas?select=*" \
-H "apikey: SUA_CHAVE_PUBLICA"
Se vier qualquer dado privado, a tabela está exposta.
Checklist de RLS
- RLS ligado em todas as tabelas do schema
public. - Nenhum
using (true)em tabela com dado pessoal ou privado. - Nenhuma policy "qualquer logado lê tudo" em dados privados.
-
with checkem todas as policies deinserteupdate. - Papéis fora do
user_metadata. - Views com
security_invoker = true. - Chave secret só no servidor.
- Índices nas colunas usadas nas policies (
user_id,org_id). - Security Advisor sem alertas críticos.
- Teste com dois usuários e com visitante deslogado feito.
Para o resto da lista de segurança (chaves, storage, rate limiting, LGPD), veja o checklist de segurança para app feito no Lovable.
Prefere que alguém revise as suas policies?
Se o seu app tem muitas tabelas, papéis diferentes ou dados sensíveis, uma segunda leitura ajuda. A Mobgeek oferece o resgate de projetos feitos com IA, com auditoria de segurança a partir de R$ 1.990, além de correções, deploy, performance e SEO. Se quiser, conte o seu caso e peça um orçamento.
Perguntas frequentes
O que é RLS no Supabase?
RLS (Row Level Security) é um recurso do Postgres que filtra, linha por linha, o que cada usuário pode ler, criar, editar ou apagar em uma tabela. No Supabase ele é essencial porque o navegador acessa o banco diretamente.
O que acontece se eu ativar o RLS sem criar policies?
Ninguém consegue acessar a tabela pela API usando a chave pública. É o estado mais seguro, mas o app para de funcionar até você criar as policies necessárias.
Policy com using (true) é errado?
Depende. Para dados realmente públicos, como um catálogo de produtos, ler com true é aceitável. Para dados pessoais ou privados, significa liberar a tabela inteira para quem a policy cobre.
A chave secret ou service_role respeita o RLS?
Não. Requisições feitas só com a chave secret ignoram o RLS, por isso ela deve ficar exclusivamente no servidor. Se ela vazar para o front-end, todas as suas policies deixam de proteger qualquer coisa.
Como testar se o meu RLS está funcionando?
Crie dois usuários de teste e tente, logado como um, ler e editar os dados do outro. Teste também deslogado, usando só a chave pública. No SQL Editor do Supabase dá para simular um usuário com set role e request.jwt.claims.
Leia também
Seu app feito no Lovable é seguro? Checklist antes de lançar
Checklist de segurança para app feito no Lovable: RLS no Supabase, autenticação, chaves, validação no servidor, storage, rate limiting e LGPD antes de lançar.
IAProblemas de sites e apps feitos com IA (Lovable, Bolt, v0)
Os problemas que ninguém conta sobre sites e apps feitos com IA: dados expostos, chaves no front-end, custos, SEO, manutenção e lock-in. Veja como evitar.
CarreiraO que é back-end: linguagens, bancos de dados e roadmap 2026
Entenda o que é back-end, o que faz um dev back-end, quais linguagens e bancos de dados aprender em 2026, como funcionam APIs e a diferença para full stack.