Google is committed to advancing racial equity for Black communities. See how.
Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Безопасный запрос данных

Эта страница основана на принципах структурирования правил безопасности и написания условий для правил безопасности, чтобы объяснить, как правила безопасности Cloud Firestore взаимодействуют с запросами. В нем более подробно рассматривается, как правила безопасности влияют на запросы, которые вы можете написать, и описывается, как обеспечить, чтобы ваши запросы использовали те же ограничения, что и ваши правила безопасности. На этой странице также описывается, как написать правила безопасности для разрешения или отклонения запросов на основе свойств запроса, таких как limit и orderBy .

Правила не являются фильтрами

При написании запросов для извлечения документов имейте в виду, что правила безопасности не являются фильтрами - запросы - это все или ничего. Чтобы сэкономить ваше время и ресурсы, Cloud Firestore сравнивает запрос с его потенциальным набором результатов вместо фактических значений полей для всех ваших документов. Если запрос может потенциально вернуть документы, которые клиент не имеет разрешения на чтение, весь запрос завершается неудачей.

Запросы и правила безопасности

Как показывают приведенные ниже примеры, вы должны написать свои запросы, чтобы соответствовать ограничениям ваших правил безопасности.

Защита и запрос документов на основе auth.uid

В следующем примере показано, как написать запрос для получения документов, защищенных правилом безопасности. Рассмотрим базу данных, которая содержит коллекцию story документов:

/ рассказы / {storyid}

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

В дополнение к полям title и content каждом документе хранятся author и published поля, которые используются для контроля доступа. В этих примерах предполагается, что приложение использует Firebase Authentication для установки в поле author UID пользователя, создавшего документ. Аутентификация Firebase также заполняет переменную request.auth в правилах безопасности.

Следующее правило безопасности использует переменные request.auth и resource.data чтобы ограничить права на чтение и запись для каждой story для ее автора:

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

Предположим, что ваше приложение содержит страницу, которая показывает пользователю список документов- story которые они создали. Вы можете ожидать, что вы можете использовать следующий запрос для заполнения этой страницы. Однако этот запрос не будет выполнен, поскольку он не включает те же ограничения, что и ваши правила безопасности:

Неверно : ограничения запроса не соответствуют ограничениям правил безопасности

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

Запрос не выполняется, даже если текущий пользователь фактически является автором каждого story документа. Причина такого поведения заключается в том, что, когда Cloud Firestore применяет ваши правила безопасности, он оценивает запрос по его потенциальному набору результатов, а не по фактическим свойствам документов в вашей базе данных. Если запрос потенциально может включать документы , которые нарушают свои правила безопасности, то запрос не будет.

Напротив, следующий запрос выполняется успешно, поскольку он включает в себя то же ограничение на поле author что и правила безопасности:

Действительный : ограничения запроса соответствуют ограничениям правил безопасности

 var user = firebase.auth().currentUser;

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

Защита и запрос документов на основе поля

Для дальнейшей демонстрации взаимодействия между запросами и правилами, правила безопасности ниже расширить доступ для чтения для stories коллекции , чтобы позволить любому пользователю читать story документов , где published поле установлено 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;
    }
  }
}
 

Запрос для опубликованных страниц должен включать те же ограничения, что и правила безопасности:

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

Ограничение запроса .where("published", "==", true) гарантирует, что resource.data.published имеет значение true для любого результата. Следовательно, этот запрос удовлетворяет правилам безопасности и ему разрешено читать данные.

in и array-contains-any запросы

При оценке предложения запроса in или array-contains-any для набора правил Cloud Firestore оценивает каждое значение сравнения отдельно. Каждое значение сравнения должно соответствовать ограничениям правил безопасности. Например, для следующего правила:

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

Invalid : Query не гарантирует, что x > 5 для всех потенциальных документов

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

Valid : Query гарантирует, что x > 5 для всех потенциальных документов

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

Оценка ограничений на запросы

Ваши правила безопасности также могут принимать или отклонять запросы на основе их ограничений. Переменная request.query содержит свойства limit , offset и orderBy запроса. Например, ваши правила безопасности могут запрещать любой запрос, который не ограничивает максимальное количество документов, извлекаемых определенным диапазоном:

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

Следующий набор правил демонстрирует, как писать правила безопасности, которые оценивают ограничения, налагаемые на запросы. В этом примере расширяется набор правил предыдущих stories со следующими изменениями:

  • Набор правил разделяет правило чтения на правила для get и list .
  • Правило get ограничивает поиск отдельных документов публичными документами или документами, созданными пользователем.
  • Правило list применяет те же ограничения, что и get но для запросов. Он также проверяет ограничение запроса, а затем отклоняет любой запрос без ограничения или с пределом, превышающим 10.
  • Набор правил определяет функцию authorOrPublished() чтобы избежать дублирования кода.
 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;
    }

  }
}
 

Коллекции групповых запросов и правила безопасности

По умолчанию запросы объединяются в одну коллекцию и получают результаты только из этой коллекции. С помощью запросов к группам коллекций вы можете получить результаты из группы коллекций, состоящей из всех коллекций с одинаковым идентификатором. В этом разделе описывается, как защитить запросы группы сбора с помощью правил безопасности.

Защита и запрос документов на основе групп сбора

В ваших правилах безопасности вы должны явно разрешить запросы групп сбора, написав правило для группы сбора:

  1. Убедитесь, что rules_version = '2'; это первая строка вашего набора правил. Для запросов групп сбора требуется новый рекурсивный подстановочный знак {name=**} правил безопасности версии 2.
  2. Напишите правило для вашей группы сбора, используя match /{path=**}/ [COLLECTION_ID] /{doc} .

Например, рассмотрим форум, организованный в виде документов forum содержащих подколлекции posts :

/ форумы / {forumid} / сообщений / {} сообщения дан

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

В этом приложении мы делаем сообщения редактируемыми их владельцами и читаемыми аутентифицированными пользователями:

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

Любой аутентифицированный пользователь может получить сообщения любого отдельного форума:

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

Но что, если вы хотите показать текущему пользователю его сообщения на всех форумах? Вы можете использовать запрос группы коллекций для получения результатов из всех коллекций posts :

 var user = firebase.auth().currentUser;

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

В ваших правилах безопасности вы должны разрешить этот запрос, написав правило чтения или списка для группы сбора 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;

    }
  }
}
 

Однако обратите внимание, что эти правила будут применяться ко всем коллекциям с идентификаторами posts , независимо от их иерархии. Например, эти правила применяются ко всем следующим коллекциям posts :

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

Безопасные запросы группы сбора на основе поля

Как и запросы с одной коллекцией, запросы группы коллекций также должны соответствовать ограничениям, установленным вашими правилами безопасности. Например, мы можем добавить published поле к каждому сообщению на форуме, как мы делали это в примере stories выше:

/ форумы / {forumid} / сообщений / {} сообщения дан

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

Затем мы можем написать правила для группы сбора posts на основе published статуса и 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;
    }
  }
}
 

С этими правилами веб-клиенты, клиенты iOS и Android могут выполнять следующие запросы:

  • Любой может получить опубликованные сообщения на форуме:

     db.collection("forums/technology/posts").where('published', '==', true).get()
     
  • Любой может получить опубликованные автором сообщения на всех форумах:

     db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
     
  • Авторы могут получить все свои опубликованные и неопубликованные сообщения на всех форумах:

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

Защита и запрос документов на основе группы сбора и пути документа

В некоторых случаях вы можете захотеть ограничить запросы групп сбора на основе пути к документу. Чтобы создать эти ограничения, вы можете использовать те же методы для защиты и запроса документов на основе поля.

Рассмотрим приложение, которое отслеживает транзакции каждого пользователя между несколькими фондовыми и криптовалютными биржами:

/ пользователей / {} идентификатор / обмен / {exchangeid} / операции / {сделка}

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

Обратите внимание на поле user . Хотя мы знаем, какой пользователь владеет документом transaction по пути документа, мы дублируем эту информацию в каждом документе transaction потому что это позволяет нам делать две вещи:

  • Запишите запросы групп сбора, которые ограничены документами, которые включают определенный /users/{userid} в свой путь документа. Например:

     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)
     
  • Примените это ограничение для всех запросов в группе сбора transactions чтобы один пользователь не мог получить документы transaction другого пользователя.

Мы применяем это ограничение в наших правилах безопасности и включаем проверку данных для 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
    }
  }
}
 

Следующие шаги