● CWE-862 · OWASP A01:2021

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 anon do 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 anon e authenticated ler e alterar todas as linhas.
USING e WITH CHECK
USING define quais linhas existentes um usuário pode ver, atualizar ou apagar; WITH CHECK define quais linhas novas ou alteradas ele pode gravar. Uma política só com USING em insert ou update deixa usuários gravarem linhas para outra organização.
Chave service_role
A chave service_role ignora 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 comparar request.auth.uid com o campo de dono do documento.

Fluxo de Ataque Passo a Paso

Passo 1

O atacante abre o app

A URL do Supabase e a chave anon estão no bundle JavaScript ou no pacote do app mobile.

Passo 2

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.

Passo 3

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.

Passo 4

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

IMPLEMENTAÇÃO VULNERÁVEL
-- 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;
PATCH SEGURO E ROBUSTO
-- 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

Fontes

← Ver o diretório completo de segurança Todos os guias de vulnerabilidades →