Catch up on everthing we announced at this year's Firebase Summit. Learn more

Bezpiecznie przeszukuj dane

Ta strona opiera się na koncepcji w strukturyzacji zasad bezpieczeństwa i pisanie Warunkami zasad bezpieczeństwa , aby wyjaśnić, jak cloud FireStore Reguły zabezpieczeń interakcji z zapytaniami. Przyjrzymy się bliżej, w jaki sposób reguły bezpieczeństwa wpływają na zapytania, które można napisać, i opisano, w jaki sposób upewnić się, że zapytania wykorzystują te same ograniczenia, co reguły zabezpieczeń. Strona ta opisuje również sposób zapisu zasad bezpieczeństwa, aby umożliwić lub zablokować zapytań w oparciu o właściwościach podobnych zapytań limit i orderBy .

Reguły to nie filtry

Pisząc zapytania w celu pobrania dokumentów, pamiętaj, że reguły bezpieczeństwa nie są filtrami — zapytania to wszystko albo nic. Aby zaoszczędzić czas i zasoby, Cloud Firestore ocenia zapytanie pod kątem potencjalnego zestawu wyników zamiast rzeczywistych wartości pól we wszystkich Twoich dokumentach. Jeśli zapytanie może potencjalnie zwrócić dokumenty, których klient nie ma uprawnień do odczytu, całe żądanie kończy się niepowodzeniem.

Zapytania i zasady bezpieczeństwa

Jak pokazują poniższe przykłady, musisz napisać swoje zapytania tak, aby pasowały do ​​ograniczeń Twoich reguł bezpieczeństwa.

Bezpieczne i zapytań w oparciu o dokumenty auth.uid

Poniższy przykład pokazuje, jak napisać zapytanie w celu pobrania dokumentów chronionych przez regułę zabezpieczeń. Rozważmy bazę danych, która zawiera zbiór story dokumentów:

/historie/{identyfikator historii}

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

Oprócz title i content pól każdy dokument przechowuje author i published pola używać do kontroli dostępu. Te przykłady zakładają, że aplikacja używa uwierzytelniania Firebase ustawić author pole do UID użytkownika, który utworzył dokument. Uwierzytelnianie Firebase zapełnia także request.auth zmienną zasad bezpieczeństwa.

Poniższa reguła bezpieczeństwa używa request.auth i resource.data zmiennych w celu ograniczenia odczytu i zapisu dla każdej story do jego autora:

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

Załóżmy, że Twoja aplikacja zawiera stronę, która pokazuje użytkownikowi listę story dokumentów, które są autorami. Można się spodziewać, że do wypełnienia tej strony można użyć następującego zapytania. Jednak to zapytanie zakończy się niepowodzeniem, ponieważ nie zawiera tych samych ograniczeń, co reguły bezpieczeństwa:

Nieprawidłowy: ograniczenia Zapytanie nie pasuje do reguł bezpieczeństwa ograniczeń

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

Zapytanie nie nawet jeśli bieżący użytkownik faktycznie jest autorem każdego story dokumentu. Powodem takiego zachowania jest to, że kiedy Chmura Firestore stosuje swoje zasady bezpieczeństwa, ocenia zapytania przed jego potencjalnego zbioru wynikowego, a nie w stosunku do rzeczywistych właściwości dokumentów w bazie danych. Jeśli zapytanie może potencjalnie zawierać dokumenty, które naruszają reguły zabezpieczeń, kwerenda nie powiedzie się.

W przeciwieństwie do tego, następujące zapytanie się powiedzie, ponieważ zawiera taką samą presję na author polu jako zasad bezpieczeństwa:

Ważny: ograniczenia zapytania pasuje do zasad bezpieczeństwa ograniczeń

var user = firebase.auth().currentUser;

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

Zabezpiecz i wyszukuj dokumenty na podstawie pola

Aby dodatkowo wykazać interakcję pomiędzy zapytaniami i reguł, zasad bezpieczeństwa poniżej poszerzyć dostęp do odczytu dla stories kolekcji, aby umożliwić każdemu użytkownikowi odczytać story dokumentów, gdzie published polowym znajduje się 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;
    }
  }
}

Zapytanie o opublikowane strony musi zawierać te same ograniczenia, co reguły bezpieczeństwa:

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

Zapytanie ograniczenie .where("published", "==", true) gwarantuje, że resource.data.published jest true dla każdego wyniku. Dlatego to zapytanie spełnia reguły bezpieczeństwa i może odczytywać dane.

in i array-contains-any zapytania

Przy ocenie in lub array-contains-any klauzuli zapytanie do zestawu reguł, Chmura Firestore ocenia każdą wartość porównawczą oddzielnie. Każda wartość porównania musi spełniać ograniczenia reguły bezpieczeństwa. Na przykład dla następującej reguły:

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

Nieprawidłowy: Zapytanie nie gwarantuje, że x > 5 dla wszystkich potencjalnych dokumentów

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

Ważny: Zapytanie gwarantuje, że x > 5 dla wszystkich potencjalnych dokumentów

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

Ocena ograniczeń w zapytaniach

Twoje reguły bezpieczeństwa mogą również akceptować lub odrzucać zapytania na podstawie ich ograniczeń. request.query zmienna zawiera limit , offset i orderBy właściwości kwerendy. Na przykład, Twoje reguły bezpieczeństwa mogą odrzucać każde zapytanie, które nie ogranicza maksymalnej liczby pobieranych dokumentów do określonego zakresu:

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

Poniższy zestaw reguł pokazuje, jak pisać reguły zabezpieczeń, które oceniają ograniczenia nałożone na zapytania. Przykład ten rozszerza wcześniejsze stories zestaw reguł z następującymi zmianami:

  • Zestaw reguł oddziela regułę do reguł odczytu get i list .
  • W get ogranicza zasada odzyskiwanie pojedynczych dokumentów do dokumentów publicznych lub dokumentach autorstwa użytkownika.
  • list reguła stosuje te same ograniczenia jak get , ale dla zapytań. Sprawdza również limit zapytań, a następnie odrzuca zapytania bez limitu lub z limitem większym niż 10.
  • Zestaw reguł określa authorOrPublished() funkcji do kodu w celu uniknięcia powielania.
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;
    }

  }
}

Zapytania grupowe i zasady bezpieczeństwa

Domyślnie zapytania są objęte zakresem jednej kolekcji i pobierają wyniki tylko z tej kolekcji. Z zapytaniami grupowych kolekcji można pobrać wyników z grupy składającej się z kolekcji wszystkich kolekcji z tym samym identyfikatorem. W tej sekcji opisano, jak zabezpieczyć zapytania grupy kolekcji przy użyciu reguł zabezpieczeń.

Zabezpieczanie i odpytywanie dokumentów w oparciu o grupy kolekcji

W swoich regułach zabezpieczeń musisz jawnie zezwolić na zapytania dotyczące grupy kolekcji, pisząc regułę dla grupy kolekcji:

  1. Upewnij rules_version = '2'; to pierwsza linia twojego zestawu reguł. Grupa zapytań Kolekcja wymagają nowego rekurencyjną wieloznaczny {name=**} zachowanie zasad bezpieczeństwa w wersji 2.
  2. Napisać regułę dla Ciebie grupę kolekcji za pomocą match /{path=**}/ [COLLECTION_ID] /{doc} .

Na przykład, rozważmy forum zorganizowanej w forum dokumentów zawierających posts podzbiorów:

/fora/{identyfikator forum}/posty/{identyfikator posta}

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

W tej aplikacji sprawiamy, że posty mogą być edytowane przez ich właścicieli i odczytywane przez uwierzytelnionych użytkowników:

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

Każdy uwierzytelniony użytkownik może pobrać posty z dowolnego pojedynczego forum:

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

Ale co, jeśli chcesz pokazać bieżącemu użytkownikowi jego posty na wszystkich forach? Można użyć zapytania grupowego kolekcja pobierać wyniki ze wszystkich posts kolekcji:

var user = firebase.auth().currentUser;

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

W swoich zasad bezpieczeństwa, należy zezwolić tej kwerendy pisząc listy do odczytu lub regułę dla posts grupy kolekcji:

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;

    }
  }
}

Należy jednak pamiętać, że przepisy te będą miały zastosowanie do wszystkich kolekcji z id posts , niezależnie od hierarchii. Na przykład, przepisy te mają zastosowanie do wszystkich następujących posts kolekcji:

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

Bezpieczne zapytania grupy kolekcji na podstawie pola

Podobnie jak kwerendy z pojedynczą kolekcją, kwerendy grupy kolekcji muszą również spełniać ograniczenia określone przez reguły zabezpieczeń. Na przykład, możemy dodać published pole do każdego postu na forum jak to zrobiliśmy w stories powyższym przykładzie:

/fora/{identyfikator forum}/posty/{identyfikator posta}

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

Możemy wtedy napisać zasady posts grupy zbiórki na podstawie published statusu i 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;
    }
  }
}

Dzięki tym regułom klienci sieci Web, Apple i Android mogą wykonywać następujące zapytania:

  • Każdy może pobrać opublikowane posty na forum:

    db.collection("forums/technology/posts").where('published', '==', true).get()
    
  • Każdy może pobrać opublikowane posty autora na wszystkich forach:

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • Autorzy mogą pobierać wszystkie swoje opublikowane i nieopublikowane posty na wszystkich forach:

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

Zabezpieczanie i odpytywanie dokumentów na podstawie grupy kolekcji i ścieżki dokumentu

W niektórych przypadkach możesz chcieć ograniczyć zapytania grup kolekcji na podstawie ścieżki dokumentu. Aby utworzyć te ograniczenia, możesz użyć tych samych technik do zabezpieczania dokumentów i odpytywania ich na podstawie pola.

Rozważ aplikację, która śledzi transakcje każdego użytkownika między kilkoma giełdami akcji i kryptowalut:

/users/{identyfikator użytkownika}/exchange/{identyfikator giełdy}/transakcje/{transakcja}

{
  amount: 100,
  exchange: 'some_exchange_name',
  timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
  user: "some_auth_id",
}

Zauważ user pole. Nawet jeśli wiemy, które użytkownik posiada transaction dokumentu ze ścieżki dokumentu, możemy powielać tych informacji w każdej transaction dokumentu, ponieważ pozwala nam zrobić dwie rzeczy:

  • Zapisu zapytań grupowych kolekcji, które są ograniczone do dokumentów, które zawierają konkretne /users/{userid} na swojej drodze dokumentu. Na przykład:

    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)
    
  • Egzekwować to ograniczenie dla wszystkich zapytań dotyczących transactions grupie zbierającej więc jeden użytkownik nie może pobrać innego użytkownika transaction dokumentów.

Mamy egzekwować to ograniczenie w naszych zasad bezpieczeństwa i obejmować walidację danych dla user polu:

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

Następne kroki