Lühidalt
Saatsin samad päringud 11 Firestore’i reeglistikule külalisena, anonüümse kasutajana ja kolme sisseloginud kasutajana. request.auth != null lasi võõral lugeda, muuta ja kustutada kõigi tellimusi. Omaniku kontroll ainult resource peal lasi kasutajal anda tellimus kellelegi teisele. Üks lai {document=**} avas kollektsiooni, millel oli õige omanikureegel. Ja dokumenti salvestatud roll, mida kasutaja saab kirjutada, tegi ta ühe päringuga adminiks. Igaühel on all parandus ja parandatud reegel pidas samade päringute vastu.
Firebase’i rakenduse brauseri ja andmebaasi vahel pole teie serverit. Projekti konfiguratsioon on lehe sees ja turvareeglid on see, mis seisab teie andmete ja kõigi vahel, kel see konfiguratsioon on. Seepärast tahtsin näiteid reeglitest koos rünnakuga, mitte nimekirja reeglitest, mis näevad õiged välja.
Kuidas testisin. Firebase Local Emulator Suite Firebase CLI 15.34.2-st, 3. novembril 2026. Laadisin iga reeglistiku, lähtestasin andmed ja kutsusin Firestore’i REST API-t päris tokenitega Auth-emulaatorist: ilma tokenita, anonüümne kasutaja, kasutajad A ja B ning võõras, kes oli ainult registreerunud. Andmed olid väljamõeldud: A-le kuuluvad tellimused a1 ja a2, B-le b1.
Millised Firebase’i reeglid lekivad?
| Reegel | Päring | Tulemus |
|---|---|---|
if true (testrežiim) |
Külaline loetleb tellimused | 200, kõik 3 |
if true (testrežiim) |
Külaline kustutab B tellimuse | 200 |
request.auth != null |
Anonüümne kasutaja loetleb tellimused | 200, kõik 3 |
request.auth != null |
Võõras kustutab A tellimuse | 200 |
Omaniku kontroll ainult resource peal |
A loob tellimuse, mille omanik on B | 200 |
Omaniku kontroll ainult resource peal |
A muudab oma tellimuse ownerId |
200 |
Omanikureegel pluss {document=**} read |
A loeb B tellimuse | 200 |
Kasutaja saab kirjutada oma users dok |
A paneb role: admin ja loeb B tellimuse |
200 |
Mida testrežiim tegelikult avab?
match /{document=**} {
allow read, write: if true;
}
Kõik, kõigile. Ilma tokenita külaline loetles kõik 3 tellimust, kustutas ühe ja lõi uue. Selleks pole vaja kontot ega lekkinud saladust, ainult projekti id-d.
Kuupäevaga versioon, mida testrežiim tihti kasutab, ebaõnnestub teisiti. Kui on if request.time < timestamp.date(2026, 1, 1) ja see kuupäev on möödas, sai külaline 403 ja nii sai ka kasutaja A oma tellimust lugedes. Leke on läbi, aga ka rakendus.
Kas request.auth != null piisab?
Ei. See ütleb ainult, et päringu taga on mingi konto.
match /orders/{id} {
allow read, write: if request.auth != null;
}
Külaline sai 403, mistõttu see reegel tundub kiires testis turvaline. Seejärel võõras, kes oli ainult registreerunud, loetles kõik 3 tellimust, muutis B tellimuse summat ja kustutas A tellimuse, kõik 200-ga. Ka anonüümne kasutaja loetles kõik 3. Kui registreerumine on lahti, tähendab see reegel “kõik, kes vaevuvad registreeruma”. Firebase’i dokumentatsioon loetleb selle samuti tavaliste ebaturvaliste mustrite hulgas.
Miks vajab omaniku kontroll nii resource kui ka request.resource?
resource on dokument sellisena, nagu see on salvestatud. request.resource on dokument sellisena, nagu see pärast kirjutamist oleks. Reegel, mis vaatab ainult esimest, kontrollib, kellele rida praegu kuulub, ja ei ütle midagi selle kohta, milleks kasutaja selle muudab.
match /orders/{id} {
allow read, update, delete: if resource.data.ownerId == request.auth.uid;
allow create: if request.auth != null;
}
Lugemised nägid õiged välja: A sai oma tellimusel 200 ja B tellimusel 403. Kirjutamised mitte. A lõi uue tellimuse, mille ownerId oli B, ja sai 200. Seejärel muutis A oma tellimuse ownerId B-ks, samuti 200, ja B sai seda sestpeale lugeda. Rakenduses, kus tellimused on arved või saldod, on see kasutaja, kes kirjutab kellegi teise kontole.
See versioon keeldus kõigest:
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;
}
| A päring | Tulemus |
|---|---|
| Loe oma tellimus | 200 |
| Loe, uuenda, kustuta B tellimus | 403 |
| Loo tellimus, mille omanik on B | 403 |
Muuda ownerId B-ks |
403 |
See lasi A-l siiski muuta oma tellimuse total, 200-ga. Kui klient ei tohi seda välja määrata, öelge, milliseid välju ta tohib muuta:
allow update: if request.auth != null
&& resource.data.ownerId == request.auth.uid
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(['note']);
Sellega andis total muutmine 403 ja note muutmine 200.
Miks mu päring ebaõnnestus õige reegliga?
Sest reeglid ei ole filtrid. Ülaltoodud omanikureegli puhul sai A, kes küsis kogu kollektsiooni, 403, kuigi A omab 3 tellimusest 2. Firestore hindab päringut selle järgi, mida see võiks tagastada, mitte selle järgi, mis seal on.
| A päring | Tulemus |
|---|---|
| Loetle kõik tellimused | 403 |
Päring ownerId == A |
200, 2 dok |
Päring ownerId == B |
403 |
Kui loend läheb reeglite karmistamise järel katki, parandage päringut. Reegli lõdvendamine, et loend toimiks, on viis, kuidas ülaltoodud laiad reeglid kirja saavad.
Kas lai match võib tühistada õige reegli?
Jah, ja just seda otsiksin esimesena projektis, kus on pikk reeglifail. Firestore lubab päringu, kui seda lubab mõni sobiv reegel.
match /orders/{id} {
allow read: if request.auth != null
&& resource.data.ownerId == request.auth.uid;
}
match /{document=**} {
allow read: if request.auth != null;
}
Esimene plokk loeb nagu hea omanikureegel. Teise ploki olemasolul luges A B tellimuse 200-ga, võõras loetles kõik 3 tellimust ja sama võõras luges A dokumendi kollektsioonis users. Esimeses plokis ei hoiata miski. Kustutage lai match; ühtegi võimalust, et hilisem reegel ligipääsu tagasi võtaks, pole.
Kuidas kasutaja endast admini teeb?
See muster tuleb ette alati, kui rollid elavad dokumendis:
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();
}
“Kasutajad saavad oma profiili muuta” ja “adminid saavad kõiki tellimusi lugeda” on kumbki eraldi mõistlikud. Koos on need õiguste eskalatsioon.
Parandus on takistada kliendil seda välja kirjutada. Kui kirjutamisreeglisse lisada request.resource.data.role == resource.data.role, andis sama päring 403 ja A jäi B tellimusest välja. Serverist määratud custom claims on teine tee, sest klient ei saa neid muuta.
Kaks asja, mis osutusid korda olevaks
Vanema reeglid ei lekki alamkollektsioonidesse. Kui /orders/{id} oli allow read igale sisseloginud kasutajale, luges A tellimuse, ja selle all olev /orders/b1/notes/n1 andis 403. See on dokumenteeritud käitumine: reeglid kehtivad ainult sobival teel.
Külaline ei lipsa läbi ka võrdlusest puuduva tokeniga. resource.data.ownerId == request.auth.uid andis külalisele 403 ka ilma eraldi request.auth != null-ita. Ma kirjutan kontrolli siiski välja, sest siis ütleb reegel seda, mida ta mõtleb.
Kuidas oma reegleid kontrollida?
Käivitage oma päringud emulaatoris külalisena, võõrana ja teise kasutajana. See paljastas iga ülaltoodud juhtumi; reeglite lugemine mitte.
Esimeseks läbivaatuseks loeb tasuta Firebase’i reeglite kontroll kleebitud reegleid teie projekti puutumata. Siinsetel reeglistikel märkis see ära testrežiimi, ainult sisselogimist nõudva ligipääsu, laia matchi ja ise kirjutatud rolli. Ta loeb ainult teksti, seega ei oska ta öelda, millised teie andmed on ega kas reegel üldse avaldati.
Teie apiKey lehel ei ole iseenesest probleem. Firebase’i veebikonfiguratsioon on kavandatult avalik ja tasuta secret scanner märgib seda nii, sest andmeid selle taga kaitsevad reeglid. Failidel on jälle teised reeglid ja leke, mida reeglid ei peata, mida testisin artiklis Firebase Storage’i reeglid.
Piirangud. Ainult Firestore, kohalikus emulaatoris, REST-i kaudu, väljamõeldud andmetel. Realtime Database kasutab teist reeglikeelt ja seda ei testitud. App Check, mis piirab, millised rakendused teie backendi kutsuda tohivad, ei kuulu siia; see ei asenda reegleid. Toodang võib emulaatorist erineda detailides, mida ma ei kontrollinud.
Pole kindel, mida teie reeglid läbi lasevad?
Loen need hea meelega koos teiega läbi. Müügijututa.
Võtke ühendust