Daten sicher abfragen

Diese Seite baut auf den Konzepten in Sicherheitsregeln strukturieren und Schreibbedingungen für Sicherheitsregeln auf, um die Interaktion von Firestore-Sicherheitsregeln mit Abfragen zu erläutern. Es wird näher untersucht, wie sich Sicherheitsregeln auf die Abfragen auswirken, die Sie schreiben können, und es wird beschrieben, wie Sie dafür sorgen, dass für Ihre Abfragen dieselben Einschränkungen wie für Ihre Sicherheitsregeln gelten. Auf dieser Seite wird auch beschrieben, wie Sie Sicherheitsregeln schreiben, um Abfragen basierend auf Abfrageattributen wie limit und orderBy zuzulassen oder abzulehnen.

Regeln sind keine Filter

Beachten Sie beim Schreiben von Abfragen zum Abrufen von Dokumenten, dass Sicherheitsregeln keine Filter sind. Abfragen dürfen keine oder alle Abfragen sein. Damit Sie Zeit und Ressourcen sparen, wertet Firestore eine Abfrage anhand der potenziellen Ergebnismenge aus und nicht die Feldwerte aller Dokumente. Wenn eine Abfrage Dokumente zurückgeben kann, für die der Client keine Leseberechtigung hat, schlägt die gesamte Anfrage fehl.

Abfragen und Sicherheitsregeln

Wie die folgenden Beispiele zeigen, müssen Sie Ihre Abfragen entsprechend den Einschränkungen Ihrer Sicherheitsregeln schreiben.

Dokumente basierend auf auth.uid schützen und abfragen

Das folgende Beispiel zeigt, wie Sie eine Abfrage schreiben, um Dokumente abzurufen, die durch eine Sicherheitsregel geschützt sind. Sehen Sie sich eine Datenbank mit einer Sammlung von story-Dokumenten an:

/stories/{storyid}

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

Zusätzlich zu den Feldern title und content werden in jedem Dokument die Felder author und published für die Zugriffssteuerung gespeichert. In diesen Beispielen wird davon ausgegangen, dass die Anwendung Firebase Authentication verwendet, um das Feld author auf die UID des Nutzers zu setzen, der das Dokument erstellt hat. Über Firebase Authentication wird auch die Variable request.auth in den Sicherheitsregeln gefüllt.

Die folgende Sicherheitsregel verwendet die Variablen request.auth und resource.data, um den Lese- und Schreibzugriff für jede story auf den Autor zu beschränken:

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

Angenommen, deine Anwendung enthält eine Seite, auf der dem Nutzer eine Liste der von ihm erstellten story-Dokumente angezeigt wird. Mit dieser Abfrage können Sie voraussichtlich die folgende Seite füllen. Diese Abfrage schlägt jedoch fehl, weil sie nicht die gleichen Einschränkungen wie Ihre Sicherheitsregeln enthält:

Ungültig: Abfrageeinschränkungen stimmen nicht mit den Sicherheitsregeln überein

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

Die Abfrage schlägt fehl, auch wenn der aktuelle Nutzer der Autor jedes story-Dokuments ist. Der Grund für dieses Verhalten besteht darin, dass bei der Anwendung Ihrer Sicherheitsregeln durch Firestore die Abfrage anhand ihrer potenziellen Ergebnismenge ausgewertet wird und nicht anhand der tatsächlichen Attribute von Dokumenten in Ihrer Datenbank. Wenn eine Abfrage Dokumente möglicherweise enthalten kann, die gegen Ihre Sicherheitsregeln verstoßen, schlägt die Abfrage fehl.

Im Gegensatz dazu ist die folgende Abfrage erfolgreich, da sie dieselbe Einschränkung für das Feld author wie die Sicherheitsregeln enthält:

Gültig: Abfrageeinschränkungen stimmen mit Sicherheitsregeln überein

var user = firebase.auth().currentUser;

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

Dokumente basierend auf einem Feld schützen und abfragen

Um die Interaktion zwischen Abfragen und Regeln weiter zu veranschaulichen, erweitern die folgenden Sicherheitsregeln den Lesezugriff für die stories-Sammlung, damit jeder Nutzer story-Dokumente lesen kann, bei denen das Feld published auf true gesetzt ist.

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

Die Abfrage für veröffentlichte Seiten muss dieselben Einschränkungen wie die Sicherheitsregeln enthalten:

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

Die Abfrageeinschränkung .where("published", "==", true) garantiert, dass resource.data.published für jedes Ergebnis true ist. Daher erfüllt diese Abfrage die Sicherheitsregeln und kann Daten lesen.

in- und array-contains-any-Abfragen

Bei der Auswertung einer in- oder array-contains-any-Abfrageklausel anhand eines Regelsatzes wertet Firestore jeden Vergleichswert separat aus. Jeder Vergleichswert muss die Einschränkungen der Sicherheitsregel erfüllen. Für die folgende Regel gilt beispielsweise:

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

Ungültig: Die Abfrage garantiert nicht, dass x > 5 für alle potenziellen Dokumente gilt

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

Gültig: Abfrage garantiert, dass x > 5 für alle potenziellen Dokumente

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

Einschränkungen bei Abfragen bewerten

Ihre Sicherheitsregeln können auch Abfragen basierend auf ihren Einschränkungen akzeptieren oder ablehnen. Die Variable request.query enthält die Attribute limit, offset und orderBy einer Abfrage. Ihre Sicherheitsregeln können beispielsweise jede Abfrage ablehnen, die die maximale Anzahl von Dokumenten begrenzt, die auf einen bestimmten Bereich abgerufen werden:

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

Der folgende Regelsatz zeigt, wie Sicherheitsregeln geschrieben werden, mit denen Einschränkungen für Abfragen ausgewertet werden. In diesem Beispiel wird der vorherige stories-Regelsatz mit den folgenden Änderungen erweitert:

  • Der Regelsatz unterteilt die Leseregel in Regeln für get und list.
  • Die Regel get beschränkt den Abruf einzelner Dokumente auf öffentliche Dokumente oder Dokumente, die der Nutzer erstellt hat.
  • Die Regel list wendet die gleichen Einschränkungen wie get, aber für Abfragen. Außerdem wird das Abfragelimit geprüft und alle Abfragen ohne Limit oder mit einem Limit von mehr als 10 abgelehnt.
  • Der Regelsatz definiert eine authorOrPublished()-Funktion, um eine Duplizierung von Code zu vermeiden.
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;
    }

  }
}

Sammlungsgruppenabfragen und Sicherheitsregeln

Abfragen beziehen sich standardmäßig auf eine einzelne Sammlung und sie rufen nur Ergebnisse aus dieser Sammlung ab. Mit Sammlungsgruppenabfragen können Sie Ergebnisse aus einer Sammlungsgruppe abrufen, die aus allen Sammlungen mit derselben ID besteht. In diesem Abschnitt wird beschrieben, wie Sie die Abfragen Ihrer Sammlungsgruppen mithilfe von Sicherheitsregeln schützen.

Dokumente basierend auf Sammlungsgruppen schützen und abfragen

In Ihren Sicherheitsregeln müssen Sie Sammlungsgruppenabfragen explizit zulassen, indem Sie eine Regel für die Sammlungsgruppe schreiben:

  1. rules_version = '2'; muss die erste Zeile des Regelsatzes sein. Sammlungsgruppenabfragen erfordern das neue rekursive Platzhalterverhalten {name=**} von Sicherheitsregeln Version 2.
  2. Schreiben Sie mit match /{path=**}/[COLLECTION_ID]/{doc} eine Regel für Ihre Sammlungsgruppe.

Angenommen, ein Forum ist in forum-Dokumente mit untergeordneten posts-Sammlungen organisiert:

/foren/{forumid}/posts/{postid}

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

In dieser Anwendung können Posts von ihren Inhabern bearbeitet und von authentifizierten Nutzern gelesen werden:

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

Jeder authentifizierte Nutzer kann die Beiträge eines einzelnen Forums abrufen:

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

Aber was ist, wenn Sie dem aktuellen Nutzer seine Beiträge in allen Foren zeigen möchten? Sie können eine Sammlungsgruppenabfrage verwenden, um Ergebnisse aus allen posts-Sammlungen abzurufen:

var user = firebase.auth().currentUser;

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

In Ihren Sicherheitsregeln müssen Sie diese Abfrage durch Schreiben einer Lese- oder Listenregel für die Sammlungsgruppe posts zulassen:

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;

    }
  }
}

Diese Regeln gelten jedoch unabhängig von der Hierarchie für alle Sammlungen mit der ID posts. Diese Regeln gelten beispielsweise für alle folgenden posts-Sammlungen:

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

Sichere Erfassung von Sammlungsgruppenabfragen basierend auf einem Feld

Ebenso wie Abfragen mit einzelnen Sammlungen müssen Sammlungsgruppenabfragen auch die Einschränkungen erfüllen, die durch Ihre Sicherheitsregeln festgelegt sind. Wir können beispielsweise jedem Forumsbeitrag wie im Beispiel stories oben ein published-Feld hinzufügen:

/foren/{forumid}/posts/{postid}

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

Wir können dann Regeln für die Sammlungsgruppe posts basierend auf dem Status published und dem Beitrag author schreiben:

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

Mit diesen Regeln können Web-, Apple- und Android-Clients die folgenden Abfragen stellen:

  • Jeder kann veröffentlichte Beiträge in einem Forum abrufen:

    db.collection("forums/technology/posts").where('published', '==', true).get()
    
  • Jeder kann die veröffentlichten Beiträge eines Autors in allen Foren abrufen:

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • Autoren können alle veröffentlichten und unveröffentlichten Beiträge in allen Foren abrufen:

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

Dokumente basierend auf Sammlungsgruppe und Dokumentpfad schützen und abfragen

In einigen Fällen möchten Sie eventuell Sammlungsgruppenabfragen anhand des Dokumentpfads einschränken. Zum Erstellen dieser Einschränkungen können Sie dieselben Techniken zum Sichern und Abfragen von Dokumenten basierend auf einem Feld verwenden.

Stellen Sie sich eine Anwendung vor, die die Transaktionen jedes Nutzers auf mehreren Aktien- und Kryptobörsen verfolgt:

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

Beachten Sie das Feld user. Obwohl wir wissen, welcher Nutzer ein transaction-Dokument aus dem Pfad des Dokuments besitzt, duplizieren wir diese Informationen in jedes transaction-Dokument, da wir auf diese Weise zwei Dinge tun können:

  • Sammlungsgruppenabfragen schreiben, die auf Dokumente beschränkt sind, die einen bestimmten /users/{userid} im Dokumentpfad enthalten. Beispiele:

    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)
    
  • Diese Einschränkung für alle Abfragen in der Sammlungsgruppe transactions erzwingen, damit ein Nutzer die transaction-Dokumente eines anderen Nutzers nicht abrufen kann.

Diese Einschränkung setzen wir in unseren Sicherheitsregeln durch und binden Datenvalidierung für das Feld user ein:

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

Mehr zur Verwendung von Google Pay erfahren