Esecuzione di query sui dati in modo sicuro
Questa pagina si basa sui concetti riportati in Struttura delle regole di sicurezza e Scrittura di condizioni per le regole di sicurezza per spiegare l'interazione delle regole di sicurezza di Firestore con le query. Approfondisce il modo in cui le regole di sicurezza influenzano le query che puoi scrivere e descrive come garantire che le query utilizzino gli stessi vincoli delle regole di sicurezza. Questa pagina descrive inoltre come scrivere regole di sicurezza per consentire o negare le query in base alle proprietà delle query come limit
e orderBy
.
Le regole non sono filtri
Quando scrivi query per recuperare documenti, tieni presente che le regole di sicurezza non sono filtri, le query sono tutto o niente. Per risparmiare tempo e risorse, Firestore valuta una query in base al suo set di risultati potenziale anziché ai valori effettivi del campo per tutti i documenti. Se una query è in grado di restituire documenti che il client non è autorizzato a leggere, l'intera richiesta ha esito negativo.
Query e regole di sicurezza
Come dimostrato dagli esempi riportati di seguito, devi scrivere le query per adattarle ai vincoli delle tue regole di sicurezza.
Proteggi ed esegui query sui documenti in base a auth.uid
L'esempio seguente mostra come scrivere una query per recuperare documenti
protetti da una regola di sicurezza. Considera un database che contiene una raccolta di story
documenti:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
Oltre ai campi title
e content
, ogni documento archivia i campi author
e published
da utilizzare per controllo dell'accesso dell'accesso. Questi esempi presuppongono che l'app utilizzi Firebase Authentication per impostare il campo author
sull'UID dell'utente che ha creato il documento. Firebase
Autenticazione completa anche la variabile request.auth
nelle
regole di sicurezza.
La seguente regola di sicurezza utilizza le variabili request.auth
e
resource.data
per limitare l'accesso in lettura e scrittura di ogni
story
al relativo autore:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Only the authenticated user who authored the document can read or write
allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Supponiamo che la tua app includa una pagina che mostra all'utente un elenco di story
documenti scritti da lui. Potresti utilizzare la seguente query per completare questa pagina. Tuttavia, questa query avrà esito negativo perché non include gli stessi vincoli delle regole di sicurezza:
Non valido: i vincoli delle query non corrispondono a quelli delle regole di sicurezza
// This query will fail
db.collection("stories").get()
La query non riesce anche se l'utente corrente è effettivamente l'autore di ogni
documento story
. Il motivo di questo comportamento è che, quando Firestore applica le regole di sicurezza, la query viene valutata in base al suo potenziale set di risultati, non in base alle proprietà effettive dei documenti nel tuo database. Se una query potrebbe potenzialmente includere documenti che violano le regole di sicurezza, avrà esito negativo.
Al contrario, la seguente query ha esito positivo perché include nel campo author
lo stesso vincolo delle regole di sicurezza:
Valida: i vincoli delle query corrispondono a quelli delle regole di sicurezza
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
Proteggi ed esegui query sui documenti in base a un campo
Per dimostrare ulteriormente l'interazione tra query e regole, le regole di sicurezza riportate di seguito espandono l'accesso in lettura per la raccolta stories
per consentire a qualsiasi utente di leggere documenti story
in cui il campo published
è impostato su true
.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Anyone can read a published story; only story authors can read unpublished stories
allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
// Only story authors can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
La query per le pagine pubblicate deve includere gli stessi vincoli delle regole di sicurezza:
db.collection("stories").where("published", "==", true).get()
Il vincolo di query .where("published", "==", true)
garantisce che
resource.data.published
sia true
per qualsiasi risultato. Pertanto, questa query soddisfa le regole di sicurezza e può leggere i dati.
OR
query
Quando valuta una query OR
logica (or
, in
o array-contains-any
) in un set di regole, Firestore valuta ogni valore di confronto separatamente. Ogni valore di confronto deve soddisfare i vincoli delle regole di sicurezza. Ad esempio, per la seguente regola:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
Non valida: la query non garantisce che x > 5
per tutti i potenziali documenti
// These queries will fail
query(db.collection("mydocuments"),
or(where("x", "==", 1),
where("x", "==", 6)
)
)
query(db.collection("mydocuments"),
where("x", "in", [1, 3, 6, 42, 99])
)
Valida: la query garantisce che
x > 5
per tutti i potenziali documenti
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
Valutazione dei vincoli delle query
Le regole di sicurezza possono anche accettare o rifiutare le query in base ai loro vincoli.
La variabile request.query
contiene le proprietà limit
, offset
e orderBy
di una query. Ad esempio, le regole di sicurezza possono negare qualsiasi query che non limita il numero massimo di documenti recuperati a un determinato intervallo:
allow list: if request.query.limit <= 10;
Il seguente set di regole mostra come scrivere regole di sicurezza per valutare i vincoli applicati alle query. Questo esempio espande il set di regole stories
precedente con le seguenti modifiche:
- Il set di regole separa la regola di lettura in regole per
get
elist
. - La regola
get
limita il recupero di singoli documenti a documenti pubblici o documenti creati dall'utente. - La regola
list
applica le stesse restrizioni diget
ma per le query. Controlla inoltre il limite di query e poi nega qualsiasi query senza limite o con un limite superiore a 10. - Il set di regole definisce una funzione
authorOrPublished()
per evitare la duplicazione del codice.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Returns `true` if the requested story is 'published'
// or the user authored the story
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
// Deny any query not limited to 10 or fewer documents
// Anyone can query published stories
// Authors can query their unpublished stories
allow list: if request.query.limit <= 10 &&
authorOrPublished();
// Anyone can retrieve a published story
// Only a story's author can retrieve an unpublished story
allow get: if authorOrPublished();
// Only a story's author can write to a story
allow write: if request.auth.uid == resource.data.author;
}
}
}
Query e regole di sicurezza del gruppo di raccolte
Per impostazione predefinita, le query hanno come ambito una singola raccolta e recuperano i risultati solo da quella raccolta. Con le query sul gruppo di raccolte, puoi recuperare i risultati da un gruppo di raccolte costituito da tutte le raccolte con lo stesso ID. Questa sezione descrive come proteggere le query del gruppo di raccolte utilizzando le regole di sicurezza.
Proteggi ed esegui query sui documenti in base ai gruppi di raccolte
Nelle regole di sicurezza, devi consentire esplicitamente le query sul gruppo di raccolte scrivendo una regola per il gruppo:
- Verifica che
rules_version = '2';
sia la prima riga del set di regole. Le query del gruppo di raccolte richiedono il comportamento del nuovo carattere jolly ricorsivo{name=**}
della versione 2 delle regole di sicurezza. - Scrivi una regola per il tuo gruppo di raccolte utilizzando
match /{path=**}/[COLLECTION_ID]/{doc}
.
Ad esempio, considera un forum organizzato in forum
documenti contenenti
posts
sottoraccolte:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
In questa applicazione, rendiamo i post modificabili dai rispettivi proprietari e leggibili dagli utenti autenticati:
service cloud.firestore {
match /databases/{database}/documents {
match /forums/{forumid}/posts/{post} {
// Only authenticated users can read
allow read: if request.auth != null;
// Only the post author can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Qualsiasi utente autenticato può recuperare i post di qualsiasi forum:
db.collection("forums/technology/posts").get()
E se volessi mostrare all'utente corrente i suoi post in tutti i forum?
Puoi utilizzare una query del gruppo di raccolte per recuperare i risultati da tutte le raccolte posts
:
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
Nelle regole di sicurezza, devi consentire questa query scrivendo una regola di lettura o di elenco per il gruppo di raccolte posts
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Authenticated users can query the posts collection group
// Applies to collection queries, collection group queries, and
// single document retrievals
match /{path=**}/posts/{post} {
allow read: if request.auth != null;
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
Tuttavia, tieni presente che queste regole verranno applicate a tutte le raccolte con ID posts
, indipendentemente dalla gerarchia. Ad esempio, queste regole si applicano a tutte le seguenti
raccolte posts
:
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
Query sul gruppo di raccolte protette in base a un campo
Come le query di raccolta singola, anche le query gruppo di raccolte devono soddisfare i vincoli impostati dalle regole di sicurezza. Possiamo aggiungere un campo published
a ogni post del forum come abbiamo fatto nell'esempio stories
riportato sopra:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
Possiamo quindi scrivere regole per il gruppo di raccolte posts
in base allo
stato published
e al post author
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Returns `true` if the requested post is 'published'
// or the user authored the post
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
match /{path=**}/posts/{post} {
// Anyone can query published posts
// Authors can query their unpublished posts
allow list: if authorOrPublished();
// Anyone can retrieve a published post
// Authors can retrieve an unpublished post
allow get: if authorOrPublished();
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth.uid == resource.data.author;
}
}
}
Con queste regole, i client Web, Apple e Android possono effettuare le seguenti query:
Chiunque può recuperare i post pubblicati in un forum:
db.collection("forums/technology/posts").where('published', '==', true).get()
Chiunque può recuperare i post pubblicati dall'autore in tutti i forum:
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
Gli autori possono recuperare tutti i loro post pubblicati e non pubblicati in tutti i forum:
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
Proteggi ed esegui query sui documenti in base al gruppo di raccolte e al percorso dei documenti
In alcuni casi potrebbe essere opportuno limitare le query del gruppo di raccolte in base al percorso del documento. Per creare queste restrizioni, puoi utilizzare le stesse tecniche per proteggere i documenti ed eseguire query in base a un campo.
Prendiamo come esempio un'applicazione che tiene traccia delle transazioni di ciascun utente tra diversi scambi di azioni e criptovalute:
/users/{userid}/exchange/{exchangeid}/transactions/{transaction}
{
amount: 100,
exchange: 'some_exchange_name',
timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
user: "some_auth_id",
}
Osserva il campo user
. Anche se sappiamo quale utente è proprietario di un documento transaction
nel percorso del documento, duplichiamo queste informazioni in ogni documento transaction
perché ci consentono di fare due cose:
Scrivi query del gruppo di raccolte limitate ai documenti che includono un valore
/users/{userid}
specifico nel percorso dei documenti. Ad esempio:var user = firebase.auth().currentUser; // Return current user's last five transactions across all exchanges db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
Applica questa limitazione a tutte le query nel gruppo di raccolte
transactions
in modo che un utente non possa recuperare i documentitransaction
di un altro utente.
Applichiamo questa limitazione nelle nostre regole di sicurezza e includiamo la convalida dei dati per il campo user
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{path=**}/transactions/{transaction} {
// Authenticated users can retrieve only their own transactions
allow read: if resource.data.user == request.auth.uid;
}
match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
// Authenticated users can write to their own transactions subcollections
// Writes must populate the user field with the correct auth id
allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
}
}
}
Passaggi successivi
- Per un esempio più dettagliato del controllo dell'accesso basato sui ruoli, vedi Protezione dell'accesso ai dati per utenti e gruppi.
- Leggi il riferimento sulle regole di sicurezza.