Catch up on highlights from Firebase at Google I/O 2023. Learn more

Bezpiecznie przeszukuj dane

Ta strona opiera się na koncepcjach w artykułach Tworzenie reguł bezpieczeństwa i Pisanie warunków dla reguł bezpieczeństwa , aby wyjaśnić, w jaki sposób reguły bezpieczeństwa Cloud Firestore wchodzą w interakcje z zapytaniami. Przyjrzymy się bliżej, w jaki sposób reguły bezpieczeństwa wpływają na zapytania, które możesz pisać, i opisano, jak upewnić się, że zapytania używają tych samych ograniczeń, co reguły bezpieczeństwa. Na tej stronie opisano również sposób pisania reguł zabezpieczeń zezwalających na zapytania lub odrzucających je na podstawie właściwości zapytania, takich jak limit i orderBy .

Reguły nie są filtrami

Podczas pisania zapytań w celu pobrania dokumentów należy pamiętać, ż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 dokumentach. Jeśli zapytanie mogłoby potencjalnie zwrócić dokumenty, których klient nie ma uprawnień do odczytu, całe żądanie zakończy się niepowodzeniem.

Zapytania i reguły bezpieczeństwa

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

Zabezpieczaj i wysyłaj zapytania do dokumentów w oparciu o auth.uid

Poniższy przykład pokazuje, jak napisać zapytanie w celu pobrania dokumentów chronionych przez regułę bezpieczeństwa. Rozważmy bazę danych zawierającą zbiór dokumentów story :

/historie/{identyfikator opowieści}

{
  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 zawiera pola author i published , których można używać do kontroli dostępu. W tych przykładach przyjęto założenie, że aplikacja korzysta z uwierzytelniania Firebase , aby ustawić pole author na identyfikator UID użytkownika, który utworzył dokument. Uwierzytelnianie Firebase wypełnia również zmienną request.auth w regułach bezpieczeństwa.

Poniższa reguła bezpieczeństwa wykorzystuje zmienne request.auth i resource.data w celu ograniczenia dostępu do odczytu i zapisu dla 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 Twoja aplikacja zawiera stronę, która pokazuje użytkownikowi listę dokumentów story , których jest autorem. Możesz oczekiwać, że możesz użyć następującego zapytania do wypełnienia tej strony. Jednak to zapytanie zakończy się niepowodzeniem, ponieważ nie zawiera tych samych ograniczeń, co reguły bezpieczeństwa:

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

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

Zapytanie kończy się niepowodzeniem , nawet jeśli bieżący użytkownik jest autorem wszystkich dokumentów story . Przyczyną takiego zachowania jest to, że kiedy Cloud Firestore stosuje Twoje reguły bezpieczeństwa, ocenia zapytanie pod kątem potencjalnego zestawu wyników, a nie rzeczywistych właściwości dokumentów w Twojej bazie danych. Jeśli kwerenda mogłaby potencjalnie zawierać dokumenty, które naruszają twoje zasady bezpieczeństwa, kwerenda zakończy się niepowodzeniem.

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

Prawidłowy : ograniczenia zapytania są zgodne z ograniczeniami reguł zabezpieczeń

var user = firebase.auth().currentUser;

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

Zabezpieczaj i wysyłaj zapytania do dokumentów na podstawie pola

Aby dokładniej zademonstrować interakcję między zapytaniami a regułami, poniższe reguły bezpieczeństwa rozszerzają dostęp do odczytu dla kolekcji stories , aby umożliwić każdemu użytkownikowi odczytywanie dokumentów story , w których pole published ma 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 publikowane strony musi zawierać te same ograniczenia, co zasady bezpieczeństwa:

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

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

OR zapytania

Podczas oceniania zapytania logicznego OR ( or , in , lub array-contains-any ) względem zestawu 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 : Query nie gwarantuje, że x > 5 dla wszystkich potencjalnych dokumentów

// 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])
    )

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

query(db.collection("mydocuments"),
      or(where("x", "==", 6),
         where("x", "==", 42)
      )
    )

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

Ocena ograniczeń dotyczących zapytań

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ą odrzucić 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 o następujące zmiany:

  • 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, których autorem jest użytkownik.
  • Reguła list stosuje te same ograniczenia co get , ale dotyczy zapytań. Sprawdza również limit zapytania, a następnie odrzuca każde zapytanie bez limitu lub z limitem większym niż 10.
  • Zestaw reguł definiuje funkcję authorOrPublished() w celu uniknięcia 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 grup kolekcji i reguły bezpieczeństwa

Domyślnie zapytania są ograniczone do pojedynczej kolekcji i pobierają wyniki tylko z tej kolekcji. Za pomocą zapytań o grupę kolekcji można pobrać wyniki z grupy kolekcji składającej się ze wszystkich kolekcji o tym samym identyfikatorze. W tej sekcji opisano sposób zabezpieczania zapytań grupy kolekcji przy użyciu reguł zabezpieczeń.

Zabezpieczaj i przeszukuj dokumenty na podstawie grup kolekcji

W swoich regułach bezpieczeństwa musisz wyraźnie zezwolić na zapytania dotyczące grupy kolekcji, pisząc regułę dla grupy kolekcji:

  1. Upewnij się rules_version = '2'; to pierwsza linia Twojego zestawu reguł. Zapytania grupy kolekcji wymagają nowego rekurencyjnego zachowania symboli wieloznacznych {name=**} reguł zabezpieczeń w wersji 2.
  2. Napisz regułę dla swojej grupy kolekcji, używając match /{path=**}/ [COLLECTION_ID] /{doc} .

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

/forums/{forumid}/posty/{postid}

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

W tej aplikacji umożliwiamy edytowanie postów przez ich właścicieli i odczytywanie 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 o grupę 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 kolekcji 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 zasady będą miały zastosowanie do wszystkich kolekcji ze posts identyfikacyjnymi, niezależnie od hierarchii. Na przykład te zasady dotyczą wszystkich następujących kolekcji posts :

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

Bezpieczne zapytania grup kolekcji na podstawie pola

Podobnie jak zapytania dotyczące pojedynczej kolekcji, zapytania dotyczące grup kolekcji muszą również spełniać ograniczenia określone przez reguły bezpieczeństwa. Na przykład możemy dodać published pole do każdego posta na forum, tak jak zrobiliśmy to w powyższym przykładzie stories :

/forums/{forumid}/posty/{postid}

{
  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 published statusu i author postu:

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

Korzystając z tych reguł, klienci WWW, 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ą pobrać wszystkie swoje opublikowane i nieopublikowane posty na wszystkich forach:

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

Zabezpiecz dokumenty i przeszukuj je na podstawie grupy kolekcji i ścieżki dokumentu

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

Rozważmy aplikację, która śledzi transakcje każdego użytkownika na kilku giełdach akcji i kryptowalut:

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

Zwróć uwagę na pole user . Chociaż wiemy, który użytkownik jest właścicielem dokumentu transaction ze ścieżki dokumentu, duplikujemy te informacje w każdym dokumencie transaction , ponieważ pozwala nam to zrobić dwie rzeczy:

  • Pisz zapytania grupy kolekcji, które są ograniczone do dokumentów zawierających 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 zbierania 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