Regras de acesso no Supabase e no Firebase (CWE-862): como a falta de Row Level Security expõe dados e como corrigir
Apps com Supabase e Firebase deixam o navegador consultar o banco diretamente com uma chave pública, então a única coisa entre um visitante e todas as linhas é a Row Level Security ou as Security Rules. Uma tabela criada em SQL sem enable row level security, ou uma regra do Firestore deixada em allow read, write: if true, pode ser lida e alterada por qualquer pessoa. Como políticas com USING e WITH CHECK restringem as linhas ao usuário logado, com um exemplo de migração PostgreSQL.
Explicação em Linguagem Simples (ELI5)
Um prédio comercial dá a todo visitante o mesmo cartão da portaria, impresso no verso do folheto. Isso não é problema desde que a porta de cada andar verifique quem você é antes de abrir. Um andar foi montado às pressas e a porta dele não tem leitor nenhum, então o cartão da portaria a abre e qualquer um entra e lê os arquivos. A chave anon do Supabase é o cartão da portaria: ela é feita para ser pública. A Row Level Security é o leitor na porta de cada andar. A correção é instalar um leitor em cada porta e dizer a ele exatamente quais arquivos cada pessoa pode ver.
Conceitos Centrais e Termos
- Chave anon pública
- A chave
anondo Supabase e a configuração web do Firebase vão dentro do pacote do app, e qualquer pessoa pode copiá-las. Elas identificam o projeto, não o usuário, então não são segredo e não protegem dados sozinhas. - Row Level Security (RLS)
- Um recurso do PostgreSQL que filtra toda consulta por meio de políticas. O Table Editor do Supabase a ativa em tabelas novas, mas tabelas criadas com SQL ou migrações começam com RLS desligada, e as permissões padrão deixam
anoneauthenticatedler e alterar todas as linhas. - USING e WITH CHECK
USINGdefine quais linhas existentes um usuário pode ver, atualizar ou apagar;WITH CHECKdefine quais linhas novas ou alteradas ele pode gravar. Uma política só comUSINGem insert ou update deixa usuários gravarem linhas para outra organização.- Chave service_role
- A chave
service_roleignora a RLS por completo. Ela só deve ficar em servidores e edge functions, nunca num navegador ou app mobile. - Regras do Firestore em modo de teste
- Projetos iniciados em modo de teste recebem regras que liberam leitura e escrita até uma data, e muitos depois viram
allow read, write: if true. As regras precisam compararrequest.auth.uidcom o campo de dono do documento.
Fluxo de Ataque Passo a Paso
O atacante abre o app
A URL do Supabase e a chave anon estão no bundle JavaScript ou no pacote do app mobile.
Ele consulta a tabela diretamente
Ele chama o endpoint REST de invoices só com a chave anon, sem passar pelas telas do app. Uma requisição a um projeto de teste com a chave anon e sem sessão de usuário ilustra o caso.
O banco devolve todas as linhas
A RLS está desligada e anon tem permissão de select, então o PostgreSQL não aplica filtro e devolve as faturas de todas as organizações.
Dados vazam ou são alterados
Com update e delete liberados também, o atacante pode alterar ou apagar registros. A CVE-2025-48757 foi atribuída em 2025 depois que pesquisadores encontraram falta de RLS expondo dados em mais de 170 apps gerados por um construtor de apps com IA.
Código-Fonte: Vulnerável vs. Seguro
-- migration.sql: a RLS nunca é ativada nesta tabela
create table public.invoices (
id bigint generated always as identity primary key,
org_id uuid not null references public.orgs (id),
customer text not null,
amount_cents bigint not null
);
-- a chave anon pública pode ler, alterar e apagar todas as linhas
grant select, insert, update, delete on public.invoices to anon, authenticated;
-- migration.sql: RLS ligada, anon revogado, uma política por comando
create table public.invoices (
id bigint generated always as identity primary key,
org_id uuid not null references public.orgs (id),
customer text not null,
amount_cents bigint not null
);
-- com a RLS ligada, sem política não há acesso
alter table public.invoices enable row level security;
revoke all on public.invoices from anon;
-- usuários veem só faturas das organizações a que pertencem
create policy "members read their org's invoices"
on public.invoices for select to authenticated
using (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));
-- linhas novas precisam ser de uma organização do usuário; sem política de update ou delete
create policy "members add invoices to their org"
on public.invoices for insert to authenticated
with check (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));
Lista de Verificação de Segurança para Engenharia
- Ative a RLS em toda tabela de schemas expostos e falhe o CI quando
select tablename from pg_tables where schemaname = 'public' and not rowsecuritydevolver alguma linha. - Escreva uma política por comando, restrita
to authenticated, comUSINGpara leituras eWITH CHECKpara inserts e updates; deixe de fora políticas de update e delete que não forem necessárias. - Revogue as permissões da tabela para
anonquando usuários anônimos não tiverem motivo para ler, e mantenha a chaveservice_rolesó em ambientes de servidor. - Ative a RLS nas tabelas lidas pelas suas políticas, como
memberships, para que usuários não se adicionem a outra organização. - Teste com dois usuários de teste: o usuário A deve receber zero linhas da organização do usuário B; no Firestore, rode testes de regras no emulador com
@firebase/rules-unit-testing.