Esegui query sui dati in modo sicuro

Questa pagina si basa sui concetti descritti in Struttura delle regole di sicurezza e Scrittura di condizioni per le regole di sicurezza per spiegare in che modo Cloud Firestore Security Rules interagisce con le query. Esamina più da vicino l'impatto delle regole di sicurezza sulle query che puoi scrivere e descrive come assicurarti che le query utilizzino gli stessi vincoli delle regole di sicurezza. Questa pagina descrive anche come scrivere regole di sicurezza per consentire o negare le query in base alle proprietà delle query come limit e orderBy.

Per l'assistenza AI, copia il prompt di seguito nel tuo agente di programmazione AI (come Gemini in Android Studio, Antigravity, Claude Code, Codex e Cursor). Puoi personalizzare il prompt e ripetere l'operazione con l'agente. Visita la pagina Skill dell'agente Firebase per informazioni dettagliate sull'installazione.

Help me use Firebase in my app.
1. Make sure that **Firebase agent skills** are installed and updated. For Claude Code, Codex, or Cursor agents, help me install the Firebase plugin. For other agents, run `npx skills add firebase/agent-skills`. Always try to use **Firebase agent skills** for Firebase-related tasks.
2. Help me write, validate, and deploy Firebase Security Rules to protect my app's Firestore data.

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, Regole valuta una query in base al suo potenziale set di risultati anziché ai valori effettivi dei campi per tutti i tuoi documenti. Se una query potrebbe potenzialmente restituire documenti che il client non ha l'autorizzazione di leggere, l'intera richiesta non va a buon fine.

Query e regole di sicurezza

Come mostrano gli esempi riportati di seguito, devi scrivere le query in modo che rispettino i vincoli delle regole di sicurezza.

Proteggi ed esegui query sui documenti in base a auth.uid

L'esempio seguente mostra come scrivere una query per recuperare i documenti protetti da una regola di sicurezza. Considera un database che contiene una raccolta di documenti story:

/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 memorizza i campi author e published da utilizzare per il controllo degli accessi. Questi esempi presuppongono che l'app utilizzi Firebase Authentication per impostare il campo author sull'UID dell'utente che ha creato il documento. Firebase Authentication compila 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 story di cui è autore. Potresti pensare di poter utilizzare la seguente query per compilare questa pagina. Tuttavia, questa query non andrà a buon fine perché non include gli stessi vincoli delle regole di sicurezza:

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

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

La query non va a buon fine anche se l'utente corrente è effettivamente l'autore di ogni documento story. Il motivo di questo comportamento è che quando Rules applica le regole di sicurezza, valuta la query rispetto al set di risultati potenziali, non rispetto alle proprietà effettive dei documenti nel database. Se una query potrebbe potenzialmente includere documenti che violano le regole di sicurezza, la query non andrà a buon fine.

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

Valida: i vincoli della query corrispondono ai vincoli 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 i 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 della 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.

OR query

Quando valuta una query OR logica (or, in o array-contains-any) rispetto a un set di regole, Rules valuta ogni valore di confronto separatamente. Ogni valore di confronto deve soddisfare i vincoli delle regole di sicurezza. Ad esempio, per la regola seguente:

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

Non valido: 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 per le query

Le regole di sicurezza possono anche accettare o rifiutare le query in base ai relativi vincoli. La variabile request.query contiene le proprietà limit, offset e orderBy di una query. Ad esempio, le 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 seguente set di regole mostra come scrivere regole di sicurezza che valutano 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 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 limitazioni di get, ma per le query. Inoltre, controlla il limite della query e rifiuta qualsiasi query senza un 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 di gruppi di raccolte e regole di sicurezza

Per impostazione predefinita, le query hanno come ambito una singola raccolta e recuperano i risultati solo da quella raccolta. Con le query dei gruppi di raccolte, puoi recuperare i risultati da un gruppo di raccolte composto da tutte le raccolte con lo stesso ID. Questa sezione descrive come proteggere le query sui gruppi 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 sui gruppi di raccolte scrivendo una regola per il gruppo di raccolte:

  1. Assicurati che rules_version = '2'; sia la prima riga del set di regole. Le query sui gruppi di raccolte richiedono il nuovo comportamento dei caratteri jolly ricorsivi {name=**} della versione 2 delle regole di sicurezza.
  2. Scrivi una regola per il tuo gruppo di raccolte utilizzando match /{path=**}/[COLLECTION_ID]/{doc}.

Ad esempio, considera un forum organizzato in documenti forum contenenti posts raccolte secondarie:

/forums/{forumid}/posts/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
}

In questa applicazione, i post sono modificabili dai 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 un singolo forum:

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

Ma cosa succede se vuoi mostrare all'utente corrente i suoi post in tutti i forum? Puoi utilizzare una query su un 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 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;

    }
  }
}

Tieni presente, tuttavia, 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}

Proteggi le query dei gruppi di raccolte in base a un campo

Come le query su una singola raccolta, anche le query sui gruppi di raccolte 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 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 eseguire 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 ed esegui query sui documenti in base al gruppo di raccolte e al percorso del documento

In alcuni casi, potresti voler limitare le query sui gruppi di raccolte in base al percorso del documento. Per creare queste limitazioni, puoi utilizzare le stesse tecniche per proteggere ed eseguire query sui documenti in base a un campo.

Considera un'applicazione che tiene traccia delle transazioni di ogni utente tra diverse borse valori 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 possiede un documento transaction dal percorso del documento, duplichiamo queste informazioni in ogni documento transaction perché ci consente di fare due cose:

  • Scrivi query di gruppi di raccolte limitate ai documenti che includono un /users/{userid} specifico nel percorso del documento. 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 sul gruppo di raccolte transactions in modo che un utente non possa recuperare i documenti transaction 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