Prejsť na obsah

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

Zobraziť ako MarkdownOtvoriť v ClaudeOtvoriť v ChatGPTOtvoriť v Perplexity

Otázka sa tiež skopíruje. Ak sa chat otvorí prázdny, vložte ju.

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 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.

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. 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.

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.
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: 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 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 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

© 2026 Denys Zaichenko. Pôvodný článok: https://madsalt.tech/sk/blog/firebase-security-rules-examples/