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

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

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

Правила не фильтры

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

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

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

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

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

/ stories / {storyid}

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

В дополнении к title и content полей, каждый документ хранит author и published поля использовать для контроля доступа. Эти примеры предполагают , что приложение использует Firebase проверку подлинности , чтобы установить author поля на UID пользователя, создавшего документ. Firebase Authentication также заполнит 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 документов, их авторство. Вы могли ожидать, что можете использовать следующий запрос для заполнения этой страницы. Однако этот запрос завершится ошибкой, потому что он не включает те же ограничения, что и ваши правила безопасности:

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

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

Запрос не даже если текущий пользователь на самом деле является автором каждого story документа. Причина такого поведения является то, что , когда облако 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 пункт запроса в отношении набора правил, Облако Firestore оценивает каждое значение сравнения отдельно. Каждое значение сравнения должно соответствовать ограничениям правил безопасности. Например, для следующего правила:

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

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

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

Действительно: Запрос гарантирует , что 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 Ruleset со следующими изменениями:

  • Набор правил разделяет правила чтения в правила для 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;
    }

  }
}

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

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

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

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

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

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

/ форумы / {forumid} / posts / {postid}

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

    }
  }
}

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

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

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

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

/ форумы / {forumid} / posts / {postid}

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

С помощью этих правил клиенты Интернета, Apple и 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()
    

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

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

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

/ users / {userid} / exchange / {exchangeid} / transaction / {transaction}

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

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