Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Bezpiecznie przeszukuj dane

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Ta strona opiera się na pojęciach zawartych w Strukturyzacji reguł bezpieczeństwa i Warunkach pisania dla reguł bezpieczeństwa , aby wyjaśnić, w jaki sposób reguły bezpieczeństwa Cloud Firestore współdziałają 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ń. Na tej stronie opisano również, jak pisać reguły zabezpieczeń zezwalające lub odrzucające zapytania na podstawie właściwości zapytania, takich jak 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.

Zabezpiecz i wyszukuj dokumenty w oparciu o 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 dokumentów story :

/historie/{identyfikator historii}

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

Oprócz pól title i content każdy dokument przechowuje pola author i published , które są używane do kontroli dostępu. W tych przykładach założono, że aplikacja używa uwierzytelniania Firebase do ustawienia pola author na identyfikator UID użytkownika, który utworzył dokument. Uwierzytelnianie Firebase wypełnia również zmienną request.auth w regułach zabezpieczeń.

Poniższa reguła bezpieczeństwa używa zmiennych request.auth i resource.data w celu ograniczenia dostępu do odczytu i zapisu każdej story do jej 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 aplikacja zawiera stronę wyświetlającą użytkownikowi listę dokumentów story , których autorem jest. 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 zapytania nie pasują do ograniczeń reguł bezpieczeństwa

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

Zapytanie kończy się niepowodzeniem , nawet jeśli bieżący użytkownik faktycznie jest autorem każdego dokumentu story . Powodem tego zachowania jest to, że gdy Cloud Firestore stosuje reguły bezpieczeństwa, ocenia zapytanie na podstawie potencjalnego zestawu wyników, a nie na podstawie rzeczywistych właściwości dokumentów w Twojej bazie danych. Jeśli zapytanie może potencjalnie zawierać dokumenty, które naruszają Twoje reguły bezpieczeństwa, zapytanie zakończy się niepowodzeniem.

W przeciwieństwie do tego, następujące zapytanie się powiedzie, ponieważ zawiera to samo ograniczenie pola author , co reguły bezpieczeństwa:

Prawidłowy : ograniczenia zapytania odpowiadają ograniczeniom reguł bezpieczeństwa

var user = firebase.auth().currentUser;

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

Zabezpiecz i wyszukuj dokumenty na podstawie pola

Aby dokładniej zademonstrować interakcję między zapytaniami a regułami, poniższe reguły zabezpieczeń rozszerzają dostęp do odczytu dla kolekcji stories , aby umożliwić każdemu użytkownikowi czytanie dokumentów story , w których published pole jest ustawione na wartość 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()

Ograniczenie zapytania .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

Podczas oceniania klauzuli in lub array-contains-any w zestawie reguł, Cloud Firestore ocenia każdą wartość porównania osobno. 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()

Prawidłowy : 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ń. Zmienna request.query zawiera właściwości limit , offset i orderBy zapytania. 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. Ten przykład rozszerza poprzedni zestaw reguł stories z następującymi zmianami:

  • Zestaw reguł rozdziela regułę odczytu na reguły get i list .
  • Reguła get ogranicza pobieranie pojedynczych dokumentów do dokumentów publicznych lub dokumentów autorstwa użytkownika.
  • Reguła list stosuje te same ograniczenia, co 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ł definiuje funkcję authorOrPublished() , aby uniknąć powielania kodu.
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. Za pomocą zapytań grup kolekcji możesz pobierać wyniki z grupy kolekcji składającej się ze wszystkich kolekcji o tym samym identyfikatorze. 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 się, że rules_version = '2'; to pierwsza linia twojego zestawu reguł. Zapytania grup kolekcji wymagają nowego, rekurencyjnego zachowania wieloznacznego {name=**} reguł zabezpieczeń w wersji 2.
  2. Napisz regułę dla swojej grupy kolekcji za pomocą match /{path=**}/ [COLLECTION_ID] /{doc} .

Rozważmy na przykład forum zorganizowane w dokumenty forum zawierające podkolekcje posts :

/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żesz użyć zapytania grupy kolekcji, aby pobrać wyniki ze wszystkich kolekcji posts :

var user = firebase.auth().currentUser;

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

W swoich regułach bezpieczeństwa musisz zezwolić na to zapytanie, pisząc regułę odczytu lub listy dla grupy zbierania 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;

    }
  }
}

Pamiętaj jednak, że te reguły będą miały zastosowanie do wszystkich kolekcji z posts ID , niezależnie od hierarchii. Na przykład te reguły dotyczą wszystkich następujących kolekcji posts :

  • /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 posta na forum, tak jak to zrobiliśmy w powyższym przykładzie stories :

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

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

Następnie możemy napisać reguły dla grupy kolekcji posts na podstawie statusu published i author posta:

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

Zwróć uwagę na pole user . Mimo że wiemy, który użytkownik jest właścicielem dokumentu transaction ze ścieżki dokumentu, powielamy te informacje w każdym dokumencie transaction , ponieważ pozwala nam to na wykonanie dwóch rzeczy:

  • Napisz zapytania grup kolekcji, które są ograniczone do dokumentów, które zawierają określony /users/{userid} w ścieżce 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)
    
  • Wymuś to ograniczenie dla wszystkich zapytań w grupie gromadzenia transactions , aby jeden użytkownik nie mógł pobrać dokumentów transaction innego użytkownika.

Egzekwujemy to ograniczenie w naszych zasadach bezpieczeństwa i uwzględniamy walidację danych dla pola 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
    }
  }
}

Następne kroki