获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Interroga i dati in modo sicuro

Questa pagina si basa sui concetti in Strutturazione delle regole di sicurezza e Scrittura delle condizioni per le regole di sicurezza per spiegare come le regole di sicurezza di Cloud Firestore interagiscono con le query. Dà un'occhiata più da vicino a come le regole di sicurezza influenzano le query che puoi scrivere e descrive come garantire che le tue query utilizzino gli stessi vincoli delle tue regole di sicurezza. Questa pagina descrive anche come scrivere regole di sicurezza per consentire o rifiutare query in base a proprietà della 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 farti risparmiare tempo e risorse, Cloud Firestore valuta una query in base al suo set di risultati potenziale anziché ai valori di campo effettivi per tutti i tuoi documenti. Se una query potrebbe potenzialmente restituire documenti che il client non ha il permesso di leggere, l'intera richiesta fallisce.

Interrogazioni e regole di sicurezza

Come dimostrano gli esempi seguenti, è necessario scrivere le query in modo che si adattino ai vincoli delle regole di sicurezza.

Proteggi e interroga i 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 documenti di story :

/storie/{storyid}

{
  title: "A Great Story",
  content: "Once upon a time...",
  author: "some_auth_id",
  published: false
}

Oltre ai campi title e content , ogni documento memorizza i campi author e published da utilizzare per il controllo degli accessi. Questi esempi presuppongono che l'app utilizzi l' autenticazione Firebase per impostare il campo author sull'UID dell'utente che ha creato il documento. Firebase Authentication popola 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 per ogni story al suo 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 documenti della story che ha creato. Potresti aspettarti di poter utilizzare la seguente query per popolare questa pagina. Tuttavia, questa query avrà esito negativo perché non include gli stessi vincoli delle regole di sicurezza:

Non valido : i vincoli di query non corrispondono ai vincoli delle regole di sicurezza

// This query will fail
db.collection("stories").get()

La query ha esito negativo anche se l'utente corrente è effettivamente l'autore di ogni documento della story . Il motivo di questo comportamento è che quando Cloud Firestore applica le tue regole di sicurezza, valuta la query rispetto al suo set di risultati potenziale , non rispetto alle proprietà effettive dei documenti nel tuo database. Se una query potrebbe potenzialmente includere documenti che violano le tue regole di sicurezza, la query avrà esito negativo.

Al contrario, la query seguente ha esito positivo perché include lo stesso vincolo sul campo author delle regole di sicurezza:

Valido : i vincoli di query corrispondono ai vincoli delle regole di sicurezza

var user = firebase.auth().currentUser;

db.collection("stories").where("author", "==", user.uid).get()

Proteggi e interroga i documenti in base a un campo

Per dimostrare ulteriormente l'interazione tra query e regole, le regole di sicurezza seguenti espandono l'accesso in lettura per la raccolta delle stories per consentire a qualsiasi utente di leggere i documenti della 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 ed è autorizzata a leggere i dati.

in e array-contains-any query

Quando si valuta una clausola di query in o array-contains-any rispetto a un set di regole, Cloud Firestore valuta ogni valore di confronto separatamente. Ogni valore di confronto deve soddisfare i vincoli della regola di sicurezza. Ad esempio, per la seguente regola:

match /mydocuments/{doc} {
  allow read: if resource.data.x > 5;
}

Non valido : la query non garantisce che x > 5 per tutti i potenziali documenti

// This query will fail
db.collection("mydocuments").where("x", "in", [1, 3, 6, 42, 99]).get()

Valido : la query garantisce che x > 5 per tutti i potenziali documenti

db.collection("mydocuments").where("x", "in", [6, 42, 99, 105, 200]).get()

Valutazione dei vincoli sulle query

Le tue regole di sicurezza possono anche accettare o rifiutare query in base ai loro vincoli. La variabile request.query contiene le proprietà limit , offset e orderBy di una query. Ad esempio, le tue regole di sicurezza possono rifiutare qualsiasi query che non limiti il ​​numero massimo di documenti recuperati a un determinato intervallo:

allow list: if request.query.limit <= 10;

Il set di regole seguente mostra come scrivere regole di sicurezza che valutano i vincoli applicati alle query. Questo esempio espande il set di regole delle stories precedenti con le seguenti modifiche:

  • Il set di regole separa la regola di lettura in regole per get e list .
  • La regola get limita il recupero di singoli documenti ai documenti pubblici o ai documenti creati dall'utente.
  • La regola list applica le stesse restrizioni di get ma per le query. Controlla anche il limite di query, quindi nega qualsiasi query senza limite o con un limite maggiore di 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 sui gruppi di raccolta e regole di sicurezza

Per impostazione predefinita, le query hanno come ambito una singola raccolta e recuperano i risultati solo da tale raccolta. Con le query sui gruppi di raccolte è possibile 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 raccolta utilizzando le regole di sicurezza.

Proteggi e interroga i documenti in base ai gruppi di raccolta

Nelle tue regole di sicurezza, devi consentire esplicitamente le query del gruppo di raccolta scrivendo una regola per il gruppo di raccolta:

  1. Assicurati rules_version = '2'; è la prima riga del tuo set di regole. Le query del gruppo di raccolta richiedono il nuovo comportamento del carattere jolly ricorsivo {name=**} delle regole di sicurezza versione 2.
  2. Scrivi una regola per il tuo gruppo di raccolta utilizzando match /{path=**}/ [COLLECTION_ID] /{doc} .

Ad esempio, considera un forum organizzato in documenti del forum contenenti sottoraccolte di posts :

/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 loro 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;
    }
  }
}

Ogni utente autenticato può recuperare i post di ogni singolo forum:

db.collection("forums/technology/posts").get()

Ma cosa succede se vuoi mostrare all'utente attuale i loro post su tutti i forum? Puoi utilizzare una query del gruppo di raccolta per recuperare i risultati da tutte le raccolte di posts :

var user = firebase.auth().currentUser;

db.collectionGroup("posts").where("author", "==", user.uid).get()

Nelle tue regole di sicurezza, devi consentire questa query scrivendo una regola di lettura o di elenco per il gruppo di raccolta dei 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;

    }
  }
}

Tieni presente, tuttavia, che queste regole si applicheranno a tutte le raccolte con posts ID , indipendentemente dalla gerarchia. Ad esempio, queste regole si applicano a tutte le seguenti raccolte di posts :

  • /posts/{postid}
  • /forums/{forumid}/posts/{postid}
  • /forums/{forumid}/subforum/{subforumid}/posts/{postid}

Query di gruppo di raccolta sicure basate su un campo

Analogamente alle query di raccolta singola, anche le query di gruppo di raccolta devono soddisfare i vincoli impostati dalle regole di sicurezza. Ad esempio, possiamo aggiungere un campo published a ogni post del forum come abbiamo fatto nell'esempio delle stories 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 raccolta dei posts in base allo stato di published e author del post:

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 da un 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 e interroga i documenti in base al gruppo di raccolta e al percorso del documento

In alcuni casi, potresti voler limitare le query del gruppo di raccolta in base al percorso del documento. Per creare queste restrizioni, puoi utilizzare le stesse tecniche per proteggere e interrogare documenti basati su un campo.

Considera 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",
}

Notare il campo user . Anche se sappiamo quale utente possiede un documento di transaction dal percorso del documento, duplichiamo queste informazioni in ogni documento di transaction perché ci consente di fare due cose:

  • Scrivi query del gruppo di raccolta limitate ai documenti che includono uno specifico /users/{userid} nel percorso del documento. Per 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)
    
  • Applicare questa restrizione per tutte le query sul gruppo di raccolta delle transactions in modo che un utente non possa recuperare i documenti delle transaction di un altro utente.

Applichiamo questa restrizione 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
    }
  }
}

Prossimi passi