---
title: "Pravidlá zabezpečenia Firebase: 11 príkladov pod útokom"
description: "Príklady pravidiel zabezpečenia Firebase testované v emulátore: čo každé pravidlo dovolilo hosťovi alebo inému používateľovi a pravidlo, ktoré únik zavrelo."
url: "https://madsalt.tech/sk/blog/firebase-security-rules-examples/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/sk/about/"
language: "sk"
published: 2026-11-03
---

# Pravidlá zabezpečenia Firebase: 11 príkladov pod útokom

TL;DR

Rovnaké požiadavky som poslal na 11 súborov pravidiel Firestore ako hosť, ako anonymný používateľ a ako traja prihlásení používatelia. `request.auth != null` dovolilo cudziemu človeku čítať, meniť a mazať objednávky všetkých. Kontrola vlastníka iba na `resource` dovolila používateľovi odovzdať objednávku niekomu inému. Jeden široký match `{document=**}` otvoril kolekciu, ktorá mala správne pravidlo pre vlastníka. A rola uložená v dokumente, ktorý môže používateľ zapisovať, z neho urobila admina jednou požiadavkou. Každý prípad má nižšie opravu a opravené pravidlo obstálo proti rovnakým požiadavkám.

Aplikácia na Firebase nemá medzi prehliadačom a databázou žiadny váš server. Konfigurácia projektu je súčasťou stránky a [pravidlá zabezpečenia](https://firebase.google.com/docs/rules/insecure-rules) sú to, čo stojí medzi každým, kto ju má, a vašimi dátami. Chcel som preto príklady pravidiel s útokom hneď vedľa, nie zoznam pravidiel, ktoré len vyzerajú správne.

**Ako som testoval.** Firebase Local Emulator Suite z Firebase CLI 15.34.2, 3. novembra 2026. Načítal som každý súbor pravidiel, obnovil dáta a volal REST API Firestore so skutočnými tokenmi z emulátora Auth: bez tokenu, anonymný používateľ, používatelia A a B a cudzí človek, ktorý sa len zaregistroval. Dáta boli vymyslené: objednávky `a1` a `a2` patria A, `b1` patrí B.

## Ktoré pravidlá Firebase prepúšťajú?

| Pravidlo                                        | Požiadavka                                   | Výsledok        |
| ----------------------------------------------- | -------------------------------------------- | --------------- |
| `if true` (testovací režim)                     | Hosť vypíše objednávky                       | `200`, všetky 3 |
| `if true` (testovací režim)                     | Hosť zmaže objednávku B                      | `200`           |
| `request.auth != null`                          | Anonymný používateľ vypíše objednávky        | `200`, všetky 3 |
| `request.auth != null`                          | Cudzí človek zmaže objednávku A              | `200`           |
| Kontrola vlastníka iba na `resource`            | A vytvorí objednávku, ktorej vlastníkom je B | `200`           |
| Kontrola vlastníka iba na `resource`            | A zmení `ownerId` vlastnej objednávky        | `200`           |
| Pravidlo vlastníka plus čítanie `{document=**}` | A číta objednávku B                          | `200`           |
| Používateľ môže zapisovať vlastný doc `users`   | A nastaví `role: admin`, číta objednávku B   | `200`           |

## Čo testovací režim v skutočnosti otvára?

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

Všetko, každému. Hosť bez tokenu vypísal všetky 3 objednávky, jednu zmazal a vytvoril novú. Nič z toho nepotrebuje účet ani uniknuté tajomstvo, stačí id projektu.

Verzia s dátumom, ktorú testovací režim často používa, zlyháva inak. S `if request.time < timestamp.date(2026, 1, 1)` a týmto dátumom v minulosti dostal hosť `403` a rovnako aj používateľ A pri čítaní vlastnej objednávky. Únik skončil, a skončila aj aplikácia.

## Stačí `request.auth != null`?

Nie. Hovorí len to, že za požiadavkou stojí nejaký účet.

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

Hosť dostal `403`, preto sa toto pravidlo v rýchlom teste zdá bezpečné. Potom cudzí človek, ktorý sa len zaregistroval, vypísal všetky 3 objednávky, zmenil sumu na objednávke B a zmazal objednávku A, všetko s `200`. Anonymný používateľ vypísal všetky 3 tiež. Ak je registrácia otvorená, toto pravidlo znamená „každý, komu sa chce zaregistrovať“. Dokumentácia Firebase ho tiež uvádza ako [bežný nebezpečný vzor](https://firebase.google.com/docs/rules/insecure-rules).

## Prečo kontrola vlastníka potrebuje `resource` aj `request.resource`?

`resource` je dokument tak, ako je uložený. `request.resource` je dokument tak, ako by vyzeral po zápise. Pravidlo, ktoré sa pozerá len na prvý, kontroluje, komu riadok patrí teraz, a nič nehovorí o tom, na čo ho prerobia.

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

Čítanie vyzeralo správne: A dostal `200` na vlastnú objednávku a `403` na objednávku B. Zápisy nie. A vytvoril novú objednávku s `ownerId` nastaveným na B a dostal `200`. Potom A zmenil `ownerId` na vlastnej objednávke na B, tiež `200`, a B ju odvtedy mohol čítať. V aplikácii, kde objednávky sú faktúry alebo zostatky, je to používateľ, ktorý zapisuje do cudzieho účtu.

Táto verzia odmietla všetko:

```
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;
}
```

| Požiadavka od A                     | Výsledok |
| ----------------------------------- | -------- |
| Prečítať vlastnú objednávku         | `200`    |
| Čítať, upraviť, zmazať objednávku B | `403`    |
| Vytvoriť objednávku patriacu B      | `403`    |
| Zmeniť `ownerId` na B               | `403`    |

Stále dovolilo A zmeniť `total` na vlastnej objednávke, s `200`. Ak klient nemá toto pole nastavovať, uveďte, ktoré polia môže meniť:

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

S týmto vrátila zmena `total` `403` a zmena `note` vrátila `200`.

## Prečo môj dotaz zlyhal so správnym pravidlom?

Pretože [pravidlá nie sú filtre](https://firebase.google.com/docs/firestore/security/rules-query). S vyššie uvedeným pravidlom pre vlastníka dostal A pri žiadosti o celú kolekciu `403`, hoci vlastní 2 z 3 objednávok. Firestore posudzuje dotaz podľa toho, čo by mohol vrátiť, nie podľa toho, čo tam je.

| Požiadavka od A           | Výsledok      |
| ------------------------- | ------------- |
| Vypísať všetky objednávky | `403`         |
| Dotaz `ownerId == A`      | `200`, 2 docs |
| Dotaz `ownerId == B`      | `403`         |

Keď sa teda po sprísnení pravidiel pokazí zoznam, opravte dotaz. Uvoľňovať pravidlo, aby zoznam fungoval, je presne spôsob, ako vznikajú tie široké pravidlá vyššie.

## Môže široký match zrušiť správne pravidlo?

Môže, a toto by som v projekte s dlhým súborom pravidiel hľadal ako prvé. Firestore [povolí požiadavku, ak ju povolí ktorékoľvek zodpovedajúce pravidlo](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;
}
```

Prvý blok vyzerá ako dobré pravidlo pre vlastníka. S druhým blokom A prečítal objednávku B s `200`, cudzí človek vypísal všetky 3 objednávky a ten istý cudzí človek prečítal dokument A v `users`. Nič v prvom bloku nevaruje. Široký match zmažte; neexistuje spôsob, ako by neskoršie pravidlo vzalo prístup späť.

## Ako sa používateľ sám urobí adminom?

Tento vzor sa objavuje všade, kde roly žijú v dokumente:

```
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();
}
```

„Používatelia môžu upravovať vlastný profil“ a „admini môžu čítať všetky objednávky“ sú každé samo osebe rozumné. Spolu tvoria eskaláciu oprávnení.

![Admin jednou požiadavkou. Používateľ A, ktorého vlastný dokument users je zapisovateľný. Predtým: Používateľ A → čítať objednávku B → 403; Zapísať vlastnú rolu: Používateľ A → role: admin → 200; Potom: Používateľ A → čítať objednávku B → 200.](https://madsalt.tech/charts/sk/firebase-security-rules-examples.svg)

*Obrázok 1. Môj test v emulátore Firebase (CLI 15.34.2), 3. novembra 2026.*

Oprava je zabrániť klientovi zapisovať toto pole. Po pridaní `request.resource.data.role == resource.data.role` k pravidlu zápisu vrátila rovnaká požiadavka `403` a A zostal mimo objednávky B. Druhou cestou sú custom claims nastavené zo servera, pretože klient ich zmeniť nemôže.

## Dve veci, ktoré dopadli dobre

Pravidlá rodiča nepresakujú do podkolekcií. S `allow read` na `/orders/{id}` pre každého prihláseného používateľa A prečítal objednávku a `/orders/b1/notes/n1` pod ňou vrátilo `403`. Je to [zdokumentované správanie](https://firebase.google.com/docs/firestore/security/rules-structure): pravidlá platia len na zhodnej ceste.

Hosť sa neprešmykne cez porovnanie s chýbajúcim tokenom. `resource.data.ownerId == request.auth.uid` vrátilo hosťovi `403` aj bez samostatného `request.auth != null`. Kontrolu aj tak píšem explicitne, lebo potom pravidlo hovorí, čo myslí.

## Ako skontrolovať vlastné pravidlá?

Spustite svoje požiadavky v emulátore ako hosť, ako cudzí človek a ako druhý používateľ. Práve to odhalilo každý prípad vyššie; čítanie pravidiel nie.

Na prvý prechod číta bezplatná [Kontrola pravidiel Firebase](https://madsalt.tech/sk/tools/firebase-rules-checker/) vložené pravidlá bez toho, aby sa dotkla vášho projektu. Na tu použitých súboroch pravidiel označila testovací režim, prístup len pre prihlásených, široký match a rolu zapísanú samotným používateľom. Číta len text, takže nepovie, ako vyzerajú vaše dáta ani či bolo pravidlo nasadené.

Váš `apiKey` na stránke sám osebe problém nie je. Webová konfigurácia Firebase je verejná zámerne a bezplatný [Skener tajných kľúčov](https://madsalt.tech/sk/tools/secret-scanner/) ju tak aj označuje, pretože dáta za ňou chránia pravidlá. Súbory sa riadia inými pravidlami, s únikom, ktorý pravidlá nezastavia, a ten som testoval v článku pravidlá Firebase Storage.

**Limity.** Iba Firestore, v lokálnom emulátore, cez REST, na vymyslených dátach. Realtime Database používa iný jazyk pravidiel a netestoval som ju. App Check, ktorý obmedzuje, ktoré aplikácie môžu volať váš backend, sem nepatrí; pravidlá nenahrádza. Produkcia sa môže od emulátora líšiť v detailoch, ktoré som nekontroloval.

Neviete, čo vaše pravidlá prepúšťajú?

Rád si ich s vami prečítam. Bez ponuky.

[Ozvite sa](https://madsalt.tech/sk/#contact)
