In breve
Ho inviato le stesse richieste a 11 set di regole Firestore, come ospite, come utente anonimo e come tre utenti autenticati. request.auth != null ha lasciato a un estraneo la possibilità di leggere, modificare ed eliminare gli ordini di tutti. Un controllo del proprietario solo su resource ha permesso a un utente di cedere un ordine a un altro. Un solo match ampio {document=**} ha aperto una collezione che aveva una regola del proprietario corretta. E un ruolo salvato in un documento che l’utente può scrivere lo ha reso admin con una sola richiesta. Ogni caso ha la sua correzione qui sotto, e la regola corretta ha retto alle stesse richieste.
Un’app Firebase non ha un suo server tra il browser e il database. La configurazione del progetto viaggia nella pagina, e le regole di sicurezza sono ciò che sta tra chiunque la possieda e i Suoi dati. Volevo quindi esempi di regole con l’attacco accanto, non un elenco di regole che sembrano giuste.
Come ho testato. Firebase Local Emulator Suite da Firebase CLI 15.34.2, il 3 novembre 2026. Ho caricato ogni set di regole, azzerato i dati e chiamato l’API REST di Firestore con veri token dell’emulatore Auth: nessun token, un utente anonimo, gli utenti A e B, e un estraneo che si era appena registrato. I dati erano inventati: gli ordini a1 e a2 di A, b1 di B.
Quali regole Firebase lasciano trapelare i dati?
| Regola | Richiesta | Risultato |
|---|---|---|
if true (modalità test) |
L’ospite elenca gli ordini | 200, tutti e 3 |
if true (modalità test) |
L’ospite elimina l’ordine di B | 200 |
request.auth != null |
L’utente anonimo elenca gli ordini | 200, tutti e 3 |
request.auth != null |
L’estraneo elimina l’ordine di A | 200 |
Controllo del proprietario solo su resource |
A crea un ordine di proprietà di B | 200 |
Controllo del proprietario solo su resource |
A cambia ownerId del proprio ordine |
200 |
Regola del proprietario più lettura {document=**} |
A legge l’ordine di B | 200 |
L’utente può scrivere il proprio doc users |
A imposta role: admin, legge l’ordine di B |
200 |
Cosa apre davvero la modalità test?
match /{document=**} {
allow read, write: if true;
}
Tutto, a tutti. Un ospite senza token ha elencato tutti e 3 gli ordini, ne ha eliminato uno e ne ha creato uno nuovo. Niente di tutto questo richiede un account o un segreto trapelato, solo l’id del progetto.
La versione con scadenza che la modalità test usa spesso fallisce in modo diverso. Con if request.time < timestamp.date(2026, 1, 1) e quella data nel passato, l’ospite ha ricevuto 403 e così l’utente A che leggeva il proprio ordine. La falla è chiusa, e con lei l’app.
request.auth != null basta?
No. Dice solo che dietro la richiesta c’è un account qualsiasi.
match /orders/{id} {
allow read, write: if request.auth != null;
}
L’ospite ha ricevuto 403, ed è per questo che questa regola sembra sicura in un test rapido. Poi un estraneo che si era solo registrato ha elencato tutti e 3 gli ordini, cambiato il totale dell’ordine di B ed eliminato quello di A, ogni volta con 200. Anche un utente anonimo ha elencato tutti e 3. Se la registrazione è aperta, questa regola significa «chiunque si prenda la briga di registrarsi». La documentazione di Firebase la elenca anch’essa tra i pattern insicuri comuni.
Perché un controllo del proprietario ha bisogno sia di resource sia di request.resource?
resource è il documento com’è salvato. request.resource è il documento come sarebbe dopo la scrittura. Una regola che guarda solo il primo verifica di chi è la riga adesso, e non dice nulla su ciò che diventa.
match /orders/{id} {
allow read, update, delete: if resource.data.ownerId == request.auth.uid;
allow create: if request.auth != null;
}
Le letture sembravano giuste: A ha ricevuto 200 sul proprio ordine e 403 su quello di B. Le scritture no. A ha creato un nuovo ordine con ownerId impostato su B, e ha ricevuto 200. A ha poi cambiato ownerId del proprio ordine in B, anche qui 200, e da quel momento B poteva leggerlo. In un’app dove gli ordini sono fatture o saldi, è un utente che scrive nell’account di qualcun altro.
Questa versione ha rifiutato tutto:
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;
}
| Richiesta di A | Risultato |
|---|---|
| Leggere il proprio ordine | 200 |
| Leggere, modificare, eliminare quello di B | 403 |
| Creare un ordine di proprietà di B | 403 |
Cambiare ownerId in B |
403 |
A poteva comunque cambiare il total del proprio ordine, con 200. Se il client non deve impostare quel campo, indichi quali campi può modificare:
allow update: if request.auth != null
&& resource.data.ownerId == request.auth.uid
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(['note']);
Con questa regola, cambiare total ha restituito 403 e cambiare note ha restituito 200.
Perché la mia query è fallita con una regola corretta?
Perché le regole non sono filtri. Con la regola del proprietario qui sopra, A che chiedeva l’intera collezione ha ricevuto 403, anche se possiede 2 ordini su 3. Firestore giudica la query da ciò che potrebbe restituire, non da ciò che c’è.
| Richiesta di A | Risultato |
|---|---|
| Elencare tutti gli ordini | 403 |
Query ownerId == A |
200, 2 doc |
Query ownerId == B |
403 |
Quindi quando un elenco si rompe dopo aver stretto le regole, corregga la query. Allargare la regola per far funzionare l’elenco è il modo in cui si scrivono le regole troppo ampie viste sopra.
Un match ampio può annullare una regola corretta?
Sì, ed è la prima cosa che cercherei in un progetto con un lungo file di regole. Firestore consente una richiesta se una qualsiasi regola corrispondente la consente.
match /orders/{id} {
allow read: if request.auth != null
&& resource.data.ownerId == request.auth.uid;
}
match /{document=**} {
allow read: if request.auth != null;
}
Il primo blocco sembra una buona regola del proprietario. Con il secondo presente, A ha letto l’ordine di B con 200, un estraneo ha elencato tutti e 3 gli ordini, e lo stesso estraneo ha letto il documento di A in users. Nulla nel primo blocco La avvisa. Elimini il match ampio; non c’è modo di far sì che una regola successiva ritiri l’accesso.
Come si rende admin un utente?
Questo schema compare ogni volta che i ruoli stanno in un 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();
}
«Gli utenti possono modificare il proprio profilo» e «gli admin possono leggere tutti gli ordini» sono entrambe ragionevoli. Insieme sono un’escalation di privilegi.
La correzione è impedire al client di scrivere quel campo. Con request.resource.data.role == resource.data.role aggiunto alla regola di scrittura, la stessa richiesta ha restituito 403 e A è rimasto fuori dall’ordine di B. I custom claim impostati da un server sono l’altra strada, perché un client non può modificarli.
Due cose che si sono rivelate a posto
Le regole su un genitore non trapelano nelle sottocollezioni. Con allow read su /orders/{id} per qualsiasi utente autenticato, A ha letto l’ordine, e /orders/b1/notes/n1 sotto di esso ha restituito 403. È il comportamento documentato: le regole si applicano solo al percorso corrispondente.
Un ospite non passa con un confronto contro un token assente. resource.data.ownerId == request.auth.uid ha restituito 403 per l’ospite anche senza un request.auth != null separato. Il controllo lo scrivo comunque, perché così la regola dice ciò che intende.
Come si controllano le proprie regole?
Esegua le Sue richieste nell’emulatore come ospite, come estraneo e come secondo utente. È ciò che ha fatto emergere ogni caso qui sopra; leggere le regole no.
Per un primo passaggio, il Controllo regole Firebase gratuito legge regole incollate senza toccare il Suo progetto. Sui set di regole di questo articolo ha segnalato la modalità test, l’accesso solo per utenti autenticati, il match ampio e il ruolo scritto dall’utente. Legge solo testo, quindi non può dirLe che aspetto abbiano i Suoi dati né se una regola è stata distribuita.
La Sua apiKey nella pagina non è di per sé il problema. La configurazione web di Firebase è pubblica per scelta, e lo scanner di segreti gratuito la segnala come tale, perché sono le regole a proteggere i dati dietro. I file seguono ancora altre regole, con una falla che le regole non possono fermare, che ho testato in regole Firebase Storage.
Limiti. Solo Firestore, nell’emulatore locale, via REST, su dati inventati. Il Realtime Database usa un altro linguaggio di regole e non è stato testato. App Check, che limita quali app possono chiamare il Suo backend, non ne fa parte; non sostituisce le regole. La produzione può differire dall’emulatore in dettagli che non ho verificato.
Non è sicuro di cosa lascino passare le Sue regole?
Le leggo volentieri insieme a Lei. Nessuna vendita.
Mi contatti