Saltar para o conteúdo

Regras de segurança do Firebase: 11 exemplos atacados

Ver como MarkdownAbrir em ClaudeAbrir em ChatGPTAbrir em Perplexity

A pergunta também é copiada. Se o chat abrir vazio, cole-a.

Em resumo

Enviei os mesmos pedidos a 11 conjuntos de regras do Firestore como convidado, como utilizador anónimo e como três utilizadores autenticados. request.auth != null deixou um estranho ler, alterar e apagar as encomendas de todos. Uma verificação de dono só em resource deixou um utilizador passar uma encomenda a outra pessoa. Um único match {document=**} abriu uma coleção que tinha uma regra de dono correta. E uma função guardada num documento que o utilizador pode escrever fê-lo administrador num só pedido. Cada caso tem abaixo a sua correção, e a regra corrigida aguentou os mesmos pedidos.

Uma app Firebase não tem um servidor seu entre o browser e a base de dados. A configuração do projeto vai na página, e as regras de segurança são o que está entre quem a tiver e os seus dados. Por isso queria exemplos de regras com o ataque ao lado, e não uma lista de regras que parecem certas.

Como testei. Firebase Local Emulator Suite do Firebase CLI 15.34.2, a 3 de novembro de 2026. Carreguei cada conjunto de regras, repus os dados e chamei a API REST do Firestore com tokens reais do emulador de Auth: sem token, um utilizador anónimo, os utilizadores A e B e um estranho que apenas se registou. Os dados eram inventados: as encomendas a1 e a2 pertencem a A, a b1 a B.

Que regras do Firebase têm fugas?

Regra Pedido Resultado
if true (modo de teste) Convidado lista as encomendas 200, as 3
if true (modo de teste) Convidado apaga a encomenda de B 200
request.auth != null Utilizador anónimo lista as encomendas 200, as 3
request.auth != null Estranho apaga a encomenda de A 200
Verificação de dono só em resource A cria uma encomenda cujo dono é B 200
Verificação de dono só em resource A altera o ownerId da própria encomenda 200
Regra de dono mais leitura em {document=**} A lê a encomenda de B 200
O utilizador pode escrever o seu doc users A define role: admin, lê a encomenda de B 200

O que abre realmente o modo de teste?

match /{document=**} {
  allow read, write: if true;
}

Tudo, a toda a gente. Um convidado sem token listou as 3 encomendas, apagou uma e criou uma nova. Nada disto precisa de uma conta nem de um segredo vazado, só do id do projeto.

A versão com data, que o modo de teste costuma usar, falha de outra maneira. Com if request.time < timestamp.date(2026, 1, 1) e essa data já no passado, o convidado recebeu 403 e o utilizador A a ler a própria encomenda também. A fuga acabou, e a app também.

request.auth != null chega?

Não. Só diz que o pedido tem alguma conta por trás.

match /orders/{id} {
  allow read, write: if request.auth != null;
}

O convidado recebeu 403, e é por isso que esta regra parece segura num teste rápido. Depois, um estranho que apenas se registou listou as 3 encomendas, mudou o total da de B e apagou a de A, tudo com 200. Um utilizador anónimo também listou as 3. Se o registo for aberto, esta regra quer dizer “todos os que se derem ao trabalho de se registar”. A documentação do Firebase também a lista como padrão inseguro comum.

Porque é que uma verificação de dono precisa de resource e de request.resource?

resource é o documento tal como está guardado. request.resource é o documento como ficaria depois da escrita. Uma regra que só olha para o primeiro verifica de quem é a linha agora, e não diz nada sobre aquilo em que a transformam.

match /orders/{id} {
  allow read, update, delete: if resource.data.ownerId == request.auth.uid;
  allow create: if request.auth != null;
}

As leituras pareciam certas: A recebeu 200 na própria encomenda e 403 na de B. As escritas não. A criou uma encomenda com ownerId igual a B e recebeu 200. Depois A mudou o ownerId da própria encomenda para B, também 200, e a partir daí B conseguia lê-la. Numa app em que encomendas são faturas ou saldos, isto é um utilizador a escrever na conta de outra pessoa.

Esta versão recusou tudo:

match /orders/{id} {
  allow read, delete: if request.auth != null
    && resource.data.ownerId == request.auth.uid;
  allow create: if request.auth != null
    && request.resource.data.ownerId == request.auth.uid;
  allow update: if request.auth != null
    && resource.data.ownerId == request.auth.uid
    && request.resource.data.ownerId == resource.data.ownerId;
}
Pedido de A Resultado
Ler a própria encomenda 200
Ler, atualizar, apagar a de B 403
Criar uma encomenda cujo dono é B 403
Mudar o ownerId para B 403

Mesmo assim A podia alterar o total da própria encomenda, com 200. Se o cliente não deve definir esse campo, diga quais pode alterar:

allow update: if request.auth != null
  && resource.data.ownerId == request.auth.uid
  && request.resource.data.diff(resource.data).affectedKeys().hasOnly(['note']);

Com isto, alterar total devolveu 403 e alterar note devolveu 200.

Porque falhou a minha consulta com uma regra correta?

Porque as regras não são filtros. Com a regra de dono acima, A a pedir a coleção inteira recebeu 403, apesar de ser dono de 2 das 3 encomendas. O Firestore avalia a consulta pelo que ela poderia devolver, não pelo que existe.

Pedido de A Resultado
Listar todas as encomendas 403
Consulta ownerId == A 200, 2 docs
Consulta ownerId == B 403

Por isso, quando uma lista deixa de funcionar depois de apertar as regras, corrija a consulta. Afrouxar a regra para a lista funcionar é como se escrevem as regras largas de cima.

Um match largo pode anular uma regra correta?

Pode, e é o que eu procuraria primeiro num projeto com um ficheiro de regras comprido. O Firestore permite um pedido se alguma regra que corresponda o permitir.

match /orders/{id} {
  allow read: if request.auth != null
    && resource.data.ownerId == request.auth.uid;
}
match /{document=**} {
  allow read: if request.auth != null;
}

O primeiro bloco parece uma boa regra de dono. Com o segundo presente, A leu a encomenda de B com 200, um estranho listou as 3 encomendas e o mesmo estranho leu o documento de A em users. Nada no primeiro bloco avisa. Apague o match largo; não há maneira de uma regra posterior retirar um acesso.

Como se torna um utilizador administrador por si próprio?

Este padrão aparece sempre que as funções vivem num documento:

function isAdmin() {
  return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin';
}
match /users/{uid} {
  allow read, write: if request.auth.uid == uid;
}
match /orders/{id} {
  allow read: if resource.data.ownerId == request.auth.uid || isAdmin();
}

“Os utilizadores podem editar o próprio perfil” e “os administradores podem ler todas as encomendas” são, cada uma, razoáveis. Juntas, são uma escalada de privilégios.

Administrador num só pedido. O utilizador A, cujo próprio documento users é gravável. Antes: Utilizador A → ler encomenda de B → 403; Escrever a própria função: Utilizador A → role: admin → 200; Depois: Utilizador A → ler encomenda de B → 200.
Figura 1. O meu teste no emulador do Firebase (CLI 15.34.2), 3 de novembro de 2026.

A correção é impedir o cliente de escrever esse campo. Com request.resource.data.role == resource.data.role acrescentado à regra de escrita, o mesmo pedido devolveu 403 e A ficou fora da encomenda de B. As custom claims definidas a partir de um servidor são a outra via, porque um cliente não as pode alterar.

Duas coisas que afinal estavam bem

As regras de um pai não passam para as subcoleções. Com allow read em /orders/{id} para qualquer utilizador autenticado, A leu a encomenda, e /orders/b1/notes/n1 por baixo devolveu 403. É o comportamento documentado: as regras só se aplicam ao caminho correspondente.

Um convidado não passa por uma comparação com um token em falta. resource.data.ownerId == request.auth.uid devolveu 403 ao convidado mesmo sem um request.auth != null à parte. Continuo a escrever a verificação, porque assim a regra diz o que quer dizer.

Como verificar as suas próprias regras?

Corra os seus pedidos no emulador como convidado, como estranho e como segundo utilizador. Foi isso que expôs todos os casos acima; ler as regras não expôs.

Para uma primeira passagem, o Verificador de regras Firebase gratuito lê regras coladas sem tocar no seu projeto. Nos conjuntos de regras daqui assinalou o modo de teste, o acesso só com sessão iniciada, o match largo e a função escrita pelo próprio utilizador. Lê só texto, por isso não lhe diz como são os seus dados nem se uma regra foi implementada.

A sua apiKey na página não é, por si só, o problema. A configuração web do Firebase é pública por desenho, e o Scanner de segredos gratuito marca-a assim, porque são as regras que protegem os dados por trás dela. Os ficheiros seguem outras regras, com uma fuga que as regras não travam, que testei em regras do Firebase Storage.

Limites. Só Firestore, no emulador local, por REST, com dados inventados. A Realtime Database usa outra linguagem de regras e não foi testada. O App Check, que limita que apps podem chamar o seu backend, não faz parte disto; não substitui as regras. A produção pode diferir do emulador em pormenores que não verifiquei.

Não tem a certeza do que as suas regras deixam passar?

Leio-as consigo com gosto. Sem proposta comercial.

Fale comigo

© 2026 Denys Zaichenko. Artigo original: https://madsalt.tech/pt/blog/firebase-security-rules-examples/