Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Interroger les données en toute sécurité

Cette page se fonde sur les concepts Structuration Règles de sécurité et conditions d' écriture pour les règles de sécurité pour expliquer comment les règles de sécurité de Cloud FireStore Interagir avec des requêtes. Il examine de plus près la manière dont les règles de sécurité affectent les requêtes que vous pouvez écrire et décrit comment vous assurer que vos requêtes utilisent les mêmes contraintes que vos règles de sécurité. Cette page décrit également comment les règles de sécurité d'écriture pour autoriser ou refuser des requêtes basées sur les propriétés de la requête comme limit et orderBy .

Les règles ne sont pas des filtres

Lorsque vous écrivez des requêtes pour récupérer des documents, gardez à l'esprit que les règles de sécurité ne sont pas des filtres : les requêtes sont tout ou rien. Pour vous faire gagner du temps et des ressources, Cloud Firestore évalue une requête par rapport à son ensemble de résultats potentiels au lieu des valeurs de champ réelles pour tous vos documents. Si une requête peut potentiellement renvoyer des documents que le client n'a pas l'autorisation de lire, la requête entière échoue.

Requêtes et règles de sécurité

Comme le montrent les exemples ci-dessous, vous devez rédiger vos requêtes pour répondre aux contraintes de vos règles de sécurité.

Les documents sécurisés et de requête sur la base auth.uid

L'exemple suivant montre comment écrire une requête pour récupérer des documents protégés par une règle de sécurité. Considérons une base de données qui contient une collection de story documents:

/histoires/{storyid}

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

En plus des title et le content des champs chaque document stocke l' author et published des champs à utiliser pour le contrôle d' accès. Ces exemples supposent l'application utilise Firebase authentification pour définir l' author champ à l'UID de l'utilisateur qui a créé le document. Firebase authentification place également la request.auth variable dans les règles de sécurité.

La règle de sécurité suivant utilise la request.auth et resource.data variables pour restreindre l' accès lecture et écriture pour chaque story à son auteur:

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

Supposons que votre application comprend une page qui montre la liste d'un utilisateur story des documents qu'ils ont rédigé. Vous pouvez vous attendre à pouvoir utiliser la requête suivante pour remplir cette page. Cependant, cette requête échouera, car elle n'inclut pas les mêmes contraintes que vos règles de sécurité :

Non valide: contraintes de requêtes ne correspondent pas à des règles de sécurité contraintes

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

La requête échoue , même si l'utilisateur actuel est en réalité l'auteur de chaque story document. La raison de ce comportement est que lorsque Nuage Firestore applique vos règles de sécurité, il évalue la requête contre son jeu de résultats potentiels, et non contre les propriétés réelles des documents dans votre base de données. Si une requête pourrait inclure des documents qui violent vos règles de sécurité, la requête échouera.

En revanche, la requête suivante réussit, car il inclut la même contrainte sur l' author domaine que les règles de sécurité:

Validité: contraintes de correspondance des requêtes règles de sécurité contraintes

var user = firebase.auth().currentUser;

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

Sécuriser et interroger des documents en fonction d'un champ

Pour démontrer l'interaction entre les requêtes et les règles, les règles de sécurité ci - dessous élargir l' accès en lecture pour les stories collection pour permettre à tout utilisateur de lire story des documents où le published champ est défini sur 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 requête pour les pages publiées doit inclure les mêmes contraintes que les règles de sécurité :

db.collection("stories").where("published", "==", true).get()

La requête contrainte .where("published", "==", true) garantit que resource.data.published est true pour tout résultat. Par conséquent, cette requête satisfait aux règles de sécurité et est autorisée à lire des données.

in et array-contains-any requêtes

En évaluant une in ou array-contains-any clause de requête sur une base de règles, Nuage Firestore évalue chaque valeur de comparaison séparée. Chaque valeur de comparaison doit respecter les contraintes de la règle de sécurité. Par exemple, pour la règle suivante :

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

Non valide: requête ne garantit pas que x > 5 pour tous les documents potentiels

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

Validité: Interrogation garantit que x > 5 pour tous les documents potentiels

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

Évaluation des contraintes sur les requêtes

Vos règles de sécurité peuvent également accepter ou refuser des requêtes en fonction de leurs contraintes. Le request.query variable contient la limit , offset , et orderBy propriétés d'une requête. Par exemple, vos règles de sécurité peuvent refuser toute requête qui ne limite pas le nombre maximal de documents récupérés à une certaine plage :

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

L'ensemble de règles suivant montre comment écrire des règles de sécurité qui évaluent les contraintes imposées aux requêtes. Cet exemple élargit les précédentes stories RuleSet avec les modifications suivantes:

  • La règle ruleset sépare la lecture en règles pour get et list .
  • Les get restreint règle la récupération des documents individuels aux documents publics ou des documents que l'utilisateur a écrits.
  • La list règle applique les mêmes restrictions que get mais pour les requêtes. Il vérifie également la limite de requête, puis refuse toute requête sans limite ou avec une limite supérieure à 10.
  • L'ensemble de règles définit une authorOrPublished() fonction d'éviter la duplication de code.
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;
    }

  }
}

Requêtes de groupe de collecte et règles de sécurité

Par défaut, les requêtes sont étendues à une seule collection et elles récupèrent les résultats uniquement à partir de cette collection. Avec les requêtes de groupe de collecte , vous pouvez récupérer les résultats d'un groupe de collecte composé de toutes les collections avec le même ID. Cette section décrit comment sécuriser vos requêtes de groupe de collecte à l'aide de règles de sécurité.

Sécurisez et interrogez les documents en fonction des groupes de collecte

Dans vos règles de sécurité, vous devez explicitement autoriser les requêtes de groupe de collecte en écrivant une règle pour le groupe de collecte :

  1. Assurez - vous que rules_version = '2'; est la première ligne de votre ensemble de règles. Requêtes de groupe Collection exigent le nouveau joker récursive {name=**} comportement des règles de sécurité la version 2.
  2. Écrire une règle pour vous groupe de collection à l' aide match /{path=**}/ [COLLECTION_ID] /{doc} .

Par exemple, pensez à un forum organisé dans le forum documents contenant les posts sous - collections:

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

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

Dans cette application, nous rendons les publications modifiables par leurs propriétaires et lisibles par les utilisateurs authentifiés :

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

Tout utilisateur authentifié peut récupérer les messages de n'importe quel forum :

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

Mais que se passe-t-il si vous souhaitez montrer à l'utilisateur actuel ses messages sur tous les forums ? Vous pouvez utiliser une requête de groupe de collecte pour récupérer les résultats de tous les posts collections:

var user = firebase.auth().currentUser;

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

Dans vos règles de sécurité, vous devez autoriser cette requête en écrivant une règle de lecture ou d'une liste pour les posts groupe de collection:

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;

    }
  }
}

Notez toutefois que ces règles seront applicables à toutes les collections avec ID posts , quelle que soit la hiérarchie. Par exemple, ces règles sont applicables à tous les suivants posts collections:

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

Requêtes de groupe de collecte sécurisées basées sur un champ

Comme les requêtes à collection unique, les requêtes de groupe de collections doivent également respecter les contraintes définies par vos règles de sécurité. Par exemple, nous pouvons ajouter un published champ à chaque poste de forum comme nous l' avons fait dans les stories exemple ci - dessus:

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

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

On peut alors écrire des règles pour les posts groupe de collecte sur la base published le statut et le poste 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;
    }
  }
}

Avec ces règles, les clients Web, iOS et Android peuvent effectuer les requêtes suivantes :

  • Tout le monde peut récupérer les messages publiés dans un forum :

    db.collection("forums/technology/posts").where('published', '==', true).get()
    
  • Tout le monde peut récupérer les messages publiés d'un auteur sur tous les forums :

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • Les auteurs peuvent récupérer tous leurs messages publiés et non publiés sur tous les forums :

    var user = firebase.auth().currentUser;
    
    db.collectionGroup("posts").where("author", "==", user.uid).get()
    

Sécurisez et interrogez les documents en fonction du groupe de collection et du chemin du document

Dans certains cas, vous souhaiterez peut-être restreindre les requêtes de groupe de collections en fonction du chemin du document. Pour créer ces restrictions, vous pouvez utiliser les mêmes techniques de sécurisation et d'interrogation des documents en fonction d'un champ.

Considérez une application qui garde une trace des transactions de chaque utilisateur parmi plusieurs bourses d'échange d'actions et de crypto-monnaie :

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

Notez l' user champ. Même si nous savons que l' utilisateur est propriétaire d' une transaction document à partir du chemin du document, nous reproduisons ces informations dans chaque transaction document parce qu'il nous permet de faire deux choses:

  • Requêtes de groupe de collection écriture qui sont limitées aux documents qui incluent un particulier /users/{userid} dans leur chemin du document. Par exemple:

    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)
    
  • Faire respecter cette restriction pour toutes les requêtes sur les transactions groupe de collecte afin qu'un utilisateur ne peut pas récupérer d'un autre utilisateur transaction des documents.

Nous appliquons cette restriction dans nos règles de sécurité et d' inclure la validation des données pour l' user champ:

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
    }
  }
}

Prochaines étapes