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
  1. Por que o banco de dados precisa de row level security?
  2. Quão comum é o problema?
  3. Como checar o seu app em cinco minutos?
  4. Como é uma policy correta?
  5. Quais erros você deve procurar?

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_admin na 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.

Fontes

  1. Supabase — Row Level Security
  2. Supabase — Understanding API keys
  3. Supabase — RLS performance and best practices
  4. Matt Palmer — Statement on CVE-2025-48757
  5. Matt Palmer — CVE-2025-48757
AIRow-level securitySecuritySupabase

Filipe Eduardo

Engenheiro de Software Sênior. Sete anos construindo produtos web e mobile de ponta a ponta e liderando os times que os entregam.

Acompanhe

Quer conversar
sobre isso?

Trabalho com problemas assim todos os dias. Me conte o seu.

Fale comigo