Row Level Security (RLS) no Supabase: proteja suas tabelas
O Supabase expõe suas tabelas do Postgres diretamente ao navegador pela Data API, autenticada com a anon key (publishable) pública. As políticas de Row Level Security (RLS, segurança em nível de linha) é que decidem quais linhas cada usuário pode ler ou alterar. Uma tabela no schema public sem RLS pode ser lida — e muitas vezes alterada — por qualquer pessoa que abra o seu site.
O que é
Todo projeto do Supabase vem com uma anon key (ou sb_publishable_) feita para ficar no seu frontend. Ela identifica o seu projeto, não o seu usuário — qualquer um pode copiá-la do seu bundle de JavaScript e enviar requisições por conta própria para https://<project>.supabase.co/rest/v1/, contornando totalmente a sua interface.
Row Level Security é um recurso do Postgres que associa políticas a uma tabela: condições SQL verificadas para cada linha em todo SELECT, INSERT, UPDATE e DELETE. Com o RLS ativado e sem políticas, nada fica acessível pela API. Cada política que você adiciona abre exatamente uma porta — por exemplo, "usuários logados podem ler as linhas cujo user_id corresponde ao próprio id".
Por que importa
Uma política ausente ou ampla demais é um vazamento de dados completo, não um bug menor: e-mails de usuários, perfis, pedidos, mensagens privadas — o que estiver na tabela — podem ser despejados com um único comando curl. Ferramentas de programação com IA costumam criar tabelas via migrações SQL, onde o RLS vem desativado por padrão, e geram políticas provisórias permissivas como using (true) só para o app funcionar.
A service_role key (sb_secret_) ignora o RLS por completo. Se ela for parar no código do cliente, todas as políticas que você escreveu deixam de importar — quem a tiver ganha acesso total de leitura e escrita ao seu banco de dados.
Como corrigir
Execute isto no editor SQL do Supabase para listar todas as tabelas do schema public exposto que ainda estão com o RLS desativado. O Security Advisor do dashboard reporta o mesmo problema como "RLS disabled in public".
select tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false;Ative o RLS em cada tabela e depois adicione uma política por operação. Restrinja as políticas ao papel authenticated e compare a coluna de dono da linha com auth.uid(). INSERTs e UPDATEs precisam de with check para que os usuários não consigam gravar linhas que pertencem a outra pessoa.
alter table public.profiles enable row level security;
create policy "Users read own profile"
on public.profiles for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Users update own profile"
on public.profiles for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);Troque políticas provisórias como using (true) por condições reais. Não baseie a autorização em user_metadata — os próprios usuários podem editá-lo; use app_metadata ou uma tabela de papéis. Views ignoram o RLS a menos que sejam criadas com security_invoker = true, e os buckets de storage precisam de políticas próprias em storage.objects.
Consulte sua API exatamente como um atacante faria: só com a chave pública e sem sessão de usuário. Se isto retornar linhas que não deveriam ser públicas, suas políticas estão abertas demais.
curl "https://<project>.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: <anon-key>" \
-H "Authorization: Bearer <anon-key>"Use a service_role key (sb_secret_) apenas em código de servidor, como rotas de API ou Edge Functions, nunca atrás de um prefixo NEXT_PUBLIC_ ou VITE_. Se ela já foi enviada ao navegador alguma vez, rotacione-a no dashboard.
FAQ
É seguro a minha anon key do Supabase ficar visível no navegador?
Sim — a anon key (publishable) foi feita para ser pública, mas só se toda tabela exposta tiver o RLS ativado com políticas corretas. A chave em si não concede nada que as suas políticas não permitam.
Ativei o RLS e agora meu app retorna resultados vazios. Quebrou?
Esse é o RLS funcionando como deveria: sem políticas, todas as linhas são negadas. Adicione políticas para as operações de que o seu app precisa, restritas aos usuários certos, em vez de desativar o RLS de novo.
O AppSafe testa minhas políticas de RLS?
O AppSafe não faz login no seu banco de dados, então não consegue avaliar suas políticas — use as verificações com SQL e curl acima. Mas ele varre o seu JavaScript em busca de chaves do Supabase. Se alguma for reportada, decodifique a parte do meio do JWT e confira o claim role: anon é o esperado no navegador; service_role é um vazamento crítico.
Seu app está afetado?
O AppSafe verifica isso e dezenas de outros problemas em um único escaneamento grátis.
Escanear meu app grátisGuias relacionados
Firebase Security Rules: pare de deixar seu banco de dados aberto
Sua config do Firebase é pública por design. Suas regras decidem quem vê os dados.
API keys expostas no JavaScript do cliente
Tudo no seu bundle é público. Trate assim.
Backups de banco de dados expostos: o que acontece quando /backup.sql está acessível
Um dump .sql acessível é a entrega completa de todo o seu banco de dados.