"Segurança no Firebase" são duas coisas que convém separar. A primeira é o modelo do Firebase: o navegador fala diretamente com a base de dados e o armazenamento, por isso não há um servidor a filtrar, e a proteção vive nas regras, no App Check e na configuração do projeto. A segunda é o que o Lovable, o Bolt ou o Cursor fazem com o Firebase ao gerar código, que é de onde vêm quase todos os buracos. Este guia explica o modelo, repassa o que a IA faz mal, deixa-te um firebase.json e umas regras base para colar, um prompt de revisão e a verificação de fora. Os dez erros de regras mais comuns, com a regra má e a boa, estão em os erros de segurança mais comuns no Firebase.
Parte 1: como funciona a segurança no Firebase
O cliente fala com os serviços, não com o teu servidor
Numa app Firebase típica não há backend próprio: o SDK do navegador lê e escreve no Firestore, no Realtime Database e no Storage diretamente. Isso significa que o objeto de configuração com apiKey e projectId é público por desenho (identifica o projeto, não autoriza nada) e que a única barreira entre um visitante e os teus dados são as regras de segurança. Cada produto tem as suas: Firestore, Realtime Database e Storage configuram-se separadamente, e uma app costuma ter aberto o que ninguém olhou.
As regras respondem a "quem pode o quê"
Uma regra recebe o pedido com o utilizador autenticado (request.auth), o documento que se quer ler ou escrever e os dados novos (request.resource.data). Com isso decide. A distinção chave é entre autenticado (há sessão) e autorizado (essa sessão pode ver esse documento): request.auth != null só verifica o primeiro, e registar-se é grátis. As regras também validam o que se escreve, para que um utilizador não se ponha isAdmin: true no próprio perfil.
O que protege além das regras
- App Check. Exige que os pedidos venham da tua app real (com reCAPTCHA ou Play Integrity) e não de um script com o teu
projectId. Trava o abuso e as faturas inesperadas por leituras massivas. - Restrição da chave de API. Na consola do Google Cloud, limita a chave aos domínios do teu site e às APIs que usas. Não substitui as regras, mas fecha uma porta.
- Auth. Domínios autorizados para o início de sessão, proteção contra enumeração de emails, verificação de email, só os métodos de acesso que realmente usas, autenticação multifator se lidas com dados sensíveis.
- Cloud Functions como servidor. Tudo o que precise de uma chave secreta (pagamentos, email, IA) vai numa função, com o segredo no Secret Manager, e o cliente chama a função. Atenção: uma função HTTP é pública para toda a internet a menos que verifiques o token do utilizador ou uses uma função callable com App Check.
- Orçamento. O Firebase cobra por leitura e por invocação. Alertas de orçamento no Google Cloud e limites nas regras (
request.query.limit) evitam que um ciclo ou um atacante te deixem uma fatura.
Hosting: o que o Firebase põe e o que não põe
O Firebase Hosting dá TLS automático, CDN e redirecionamento para HTTPS. Não põe cabeçalhos de segurança: acrescentam-se no firebase.json. E serve tudo o que esteja na pasta pública, incluindo um .env copiado por engano.
Parte 2: o que a IA faz mal com o Firebase
- Regras em modo de teste, e depois
if true. A IA deixa as regras que abrem tudo durante trinta dias; ao expirarem, a app falha e a solução rápida é abri-las para sempre. request.auth != nullcomo única condição. Qualquer utilizador registado lê os dados de todos os outros.- Escritas sem validar. O perfil tem
roleoucreditse a regra de update não exclui esses campos. - Outras chaves no bundle. Junto à configuração do Firebase aparecem a chave secreta do Stripe, a da OpenAI ou a de um serviço de email. Detalhe em segredos que a tua app deixa fugir no navegador.
- Funções HTTP sem verificar o utilizador. A função que cobra, envia emails ou chama um modelo responde a qualquer pessoa que conheça o URL.
- Storage com regras copiadas do Firestore.
allow read, write: if request.auth != nullemstorage.rules: qualquer utilizador lê os ficheiros dos outros e carrega o que quiser. - Sem App Check, sem cabeçalhos, sem alertas. Três caixas que ninguém marca porque a app "já funciona".
Um bloco para colar no teu projeto
Cabeçalhos de segurança no firebase.json (a Content-Security-Policy vai à parte porque depende dos teus scripts):
{
"hosting": {
"public": "dist",
"headers": [
{
"source": "**",
"headers": [
{ "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=()" }
]
}
]
}
}
E um ponto de partida para firestore.rules: tudo fechado por defeito, cada utilizador o seu documento e campos de privilégio protegidos.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null && request.auth.uid == userId
&& !request.resource.data.keys().hasAny(['role', 'isAdmin', 'plan', 'credits']);
allow update: if request.auth != null && request.auth.uid == userId
&& !request.resource.data.diff(resource.data).affectedKeys()
.hasAny(['role', 'isAdmin', 'plan', 'credits']);
}
match /{document=**} {
allow read, write: if false;
}
}
}
Acrescenta depois uma regra explícita por coleção; a última, com if false, garante que qualquer coleção nova nasce fechada.
Um prompt de revisão antes de publicar
Revê a segurança desta app com Firebase e não mudes nada ainda. Responde com uma lista:
1. Regras do Firestore, Realtime Database e Storage que permitam acesso a qualquer pessoa, com data de modo de teste ou com request.auth != null como única condição.
2. Regras de escrita que não validem os campos, em especial role, isAdmin, plan ou credits.
3. Chaves que não sejam a configuração do Firebase presentes no código do navegador, com ficheiro e linha.
4. Cloud Functions HTTP que não verifiquem o token do utilizador, e funções que leiam segredos do código em vez do Secret Manager.
5. Se o App Check está ativado e se a chave de API está restringida por domínio.
6. Cabeçalhos de segurança no firebase.json e ficheiros na pasta pública que não devam estar lá.
Para cada ponto, propõe a regra ou a alteração exata e espera pela minha confirmação.
Verifica o resultado de fora
O teu projectId está no bundle, por isso qualquer pessoa pode fazer isto. A API REST do Firestore sem token:
curl -s "https://firestore.googleapis.com/v1/projects/O_TEU_PROJETO/databases/(default)/documents/users" | head -c 400
curl -sI https://oteudominio.com | grep -iE "strict-transport|x-frame|x-content-type"
O correto é um erro de permissões na primeira (se devolver documentos, a coleção está aberta) e os cabeçalhos na segunda. Repete a primeira com cada coleção e com o Storage. O Scanaris faz estas verificações e cerca de quarenta mais em 30 segundos: deteta a configuração do Firebase no teu bundle, testa as coleções habituais sem autenticação, procura outras chaves no JavaScript, revê cabeçalhos, CORS, cookies e ficheiros expostos, e dá-te um fix prompt por cada achado para colar no Lovable, no Bolt ou no Cursor. Se a tua app usa Supabase em vez de Firebase, o guia do Supabase é o equivalente.
Perguntas frequentes
Tenho de esconder a apiKey do Firebase?
Não. É pública por desenho e identifica o projeto. O que protege os dados são as regras e o App Check. O que convém é restringi-la aos teus domínios no Google Cloud.
As Cloud Functions são privadas?
Uma função HTTP é um URL público. Verifica o token do utilizador dentro da função ou usa funções callable com App Check. As funções que reagem a eventos (um documento criado, um ficheiro carregado) não têm URL e são de facto internas.
Como testo as regras sem partir a app?
Com o simulador da consola (separador Regras) para testar leituras concretas, e com o emulador local para testes automáticos. Muda as regras com a app aberta noutro separador e verifica que o que deve funcionar continua a funcionar.