RLS no Supabase: seu app construído com IA está vazando dados?
Apps construídos com IA costumam ir ao ar com row level security fraca no Supabase. Como checar o seu em cinco minutos e como é uma policy correta.
Nesta página · 5 seções
Principais pontos
- Um app construído sobre o Supabase fala com o banco de dados direto do navegador. O row level security (RLS) é a única coisa que impede um usuário de ler os dados de todo mundo.
- Quando pesquisadores analisaram 1.645 projetos do Lovable em 2025, 170 deles tinham falhas de RLS que expunham dados para qualquer pessoa.
- Você consegue testar o seu próprio app em cinco minutos com a chave pública e uma requisição. Se ela retornar linhas que não são suas, corrija isso antes de qualquer outra coisa.
A maioria dos apps construídos com Lovable, Bolt e ferramentas de IA parecidas usa o Supabase como backend. É uma boa escolha: um banco Postgres de verdade, autenticação e uma API prontos para usar. Mas ele vem com uma regra que as ferramentas de IA nem sempre seguem, e quando não seguem, o resultado não é um bug. É um vazamento de dados.
Este post explica essa regra, como verificar se o seu app a segue e como é uma policy correta. Ele faz parte do guia para deixar um protótipo construído com IA pronto para usuários reais.
Por que o banco de dados precisa de row level security?
Porque o frontend do seu app fala com ele diretamente. Um app com Supabase leva uma publishable key (projetos mais antigos a chamam de anon key) dentro do JavaScript que todo visitante baixa. Isso é proposital: o Supabase diz que a publishable key pode ser exposta com segurança, e acrescenta que “o Row Level Security decide o que esse cliente pode alcançar, então habilite-o em todas as tabelas antes do deploy” (Supabase).
Em outras palavras, a chave só identifica o seu projeto. O que um visitante pode ler ou alterar é decidido inteiramente pelas policies de RLS de cada tabela. Sem elas, a documentação do Supabase é direta: “Uma tabela em um schema exposto sem RLS pode ser lida e escrita por qualquer role que tenha um grant sobre ela.”
Quão comum é o problema?
Comum o bastante para ganhar um CVE. Em março de 2025, o pesquisador Matt Palmer analisou 1.645 projetos construídos com Lovable e encontrou 170 deles, cerca de 10%, com RLS inadequado: 303 endpoints expostos, encontrados só olhando as páginas iniciais dos projetos (declaração sobre o CVE-2025-48757). Os dados expostos incluíam nomes e emails, chaves de API de serviços de terceiros e detalhes de pagamento e assinatura (CVE-2025-48757).
Desde então o Lovable adicionou um scanner de segurança, mas o risco de fundo não é exclusivo de uma ferramenta. Qualquer app em que o navegador fala com o Supabase depende de as policies estarem certas, e as ferramentas de IA escrevem essas policies do mesmo jeito que escrevem todo o resto: até o app funcionar.
Como checar o seu app em cinco minutos?
1. Liste as tabelas sem RLS. No editor SQL do Supabase, rode:
select tablename, rowsecurity
from pg_tables
where schemaname = 'public';Qualquer linha com rowsecurity = false é uma tabela que qualquer pessoa com a sua chave pública pode ler e escrever.
2. Teste como um estranho. Pegue a publishable key do seu site (ela está no JavaScript) e faça uma requisição direto a uma tabela, sem fazer login:
curl 'https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*' \
-H "apikey: YOUR_PUBLISHABLE_KEY"Se isso retornar perfis de outras pessoas, a requisição de qualquer outra pessoa também vai retornar.
3. Teste como outro usuário. Faça login como usuário A, copie o ID de um registro que pertence ao usuário B e peça por ele. Você não deve receber nada.
4. Leia o Security Advisor. O dashboard do Supabase sinaliza tabelas sem RLS e outros erros comuns. Ele não pega uma policy permissiva demais, e é por isso que os passos 2 e 3 importam.
Como é uma policy correta?
Para uma tabela em que cada linha pertence a um usuário, o padrão é: RLS ligado e uma policy por ação que compara o dono da linha com o usuário logado.
alter table public.projects enable row level security;
create policy "Owners read their projects"
on public.projects for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Owners create their projects"
on public.projects for insert
to authenticated
with check ((select auth.uid()) = user_id);Adicione policies de update e delete do mesmo jeito e crie um índice em user_id. A própria orientação do Supabase é indexar as colunas que as suas policies usam e envolver auth.uid() em um select, o que nos testes deles reduziu uma query de 179 ms para 9 ms (Supabase).
Quais erros você deve procurar?
using (true). Uma policy que permite tudo é o mesmo que nenhuma policy. As ferramentas de IA escrevem isso para fazer um erro sumir.- RLS desligado para “consertar” o app. Ligar o RLS sem policies bloqueia todo o acesso pela API, então o app quebra. A correção rápida que vem em seguida muitas vezes é desligá-lo de novo.
- Insert e update sem
with check. Aí os usuários conseguem criar linhas que dizem pertencer a outra pessoa. - Papéis guardados onde os usuários podem editá-los. Uma coluna
is_adminna própria linha de perfil do usuário, com uma policy de update que deixa o usuário editar a sua linha, transforma qualquer um em admin. - A chave secreta no frontend. A chave secreta (service role) do Supabase ignora todas as policies. Se ela está no navegador, o RLS não faz diferença. Mais sobre isso no próximo post desta série.
Perguntas frequentes
É seguro colocar a anon key do Supabase no meu frontend?
Sim, a publishable key (anon) foi feita para ser pública. Ela só é segura porque o row level security controla o que ela pode alcançar, então toda tabela exposta pela API precisa ter o RLS habilitado e policies que correspondam a quem deve ver cada linha.
Como sei se o meu app no Lovable tem o problema de RLS?
Verifique se o RLS está habilitado em todas as tabelas do schema public e depois faça uma requisição a uma tabela usando só a publishable key, sem login. Se voltarem linhas que deveriam ser privadas, as suas policies estão faltando ou são permissivas demais.
Habilitar o RLS quebra o meu app?
Pode quebrar, no começo. Com o RLS ligado e sem policies, a API não retorna nada, então as páginas que leem aquela tabela ficam vazias. Essa é a falha segura. A correção é adicionar policies para cada ação, não desligar o RLS de novo.