---
title: "Regras de segurança do Firebase: 11 exemplos atacados"
description: "Exemplos de regras de segurança do Firebase testados: o que cada regra deixou fazer a um convidado ou a outro utilizador e a regra que fechou a fuga."
url: "https://madsalt.tech/pt/blog/firebase-security-rules-examples/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/pt/about/"
language: "pt"
published: 2026-11-03
---

# Regras de segurança do Firebase: 11 exemplos atacados

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](https://firebase.google.com/docs/rules/insecure-rules) 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](https://firebase.google.com/docs/rules/insecure-rules).

## 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](https://firebase.google.com/docs/firestore/security/rules-query). 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](https://firebase.google.com/docs/firestore/security/rules-structure).

```
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.](https://madsalt.tech/charts/pt/firebase-security-rules-examples.svg)

*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](https://firebase.google.com/docs/firestore/security/rules-structure): 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](https://madsalt.tech/pt/tools/firebase-rules-checker/) 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](https://madsalt.tech/pt/tools/secret-scanner/) 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](https://madsalt.tech/pt/#contact)
