Como consultar dados com segurança
Esta página foi desenvolvida com base nos conceitos de
Como estruturar regras de segurança e
Como escrever condições para regras de segurança para explicar como
as regras de segurança do Cloud Firestore interagem com as consultas. Vamos examinar em detalhes como as regras de segurança afetam as consultas que você pode escrever. Além disso, vamos saber como garantir que suas consultas usem as mesmas restrições que suas regras de segurança. Depois, também veremos como escrever regras de segurança para permitir ou negar consultas com base nas propriedades dela, como limit
e orderBy
.
Regras não são filtros
Ao escrever consultas para recuperar documentos, lembre-se de que regras de segurança não são filtros. As consultas podem falhar ou serem bem-sucedidas. Para economizar tempo e recursos, o Firestore avalia uma consulta em relação ao potencial conjunto de resultados em vez dos valores de campo reais de todos os seus documentos. Se for possível que uma consulta retorne documentos para os quais o cliente não tem permissão de leitura, toda a solicitação falhará.
Consultas e regras de segurança
Como demonstrado nos exemplos abaixo, é necessário escrever consultas que atendam às restrições das suas regras de segurança.
Proteger e consultar documentos com base no auth.uid
Veja no exemplo a seguir como escrever uma consulta para recuperar documentos protegidos por uma regra de segurança. Considere um banco de dados que contém uma coleção de documentos story
:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
Além dos campos title
e content
, cada documento armazena os campos author
e published
para usar no controle de acesso. Após analisar os exemplos, é possível concluir que o aplicativo usa o Firebase Authentication para definir o campo author
como o UID do usuário que criou o documento. O Firebase Authentication também preenche a variável request.auth
nas regras de segurança.
Na regra de segurança a seguir, são usadas as variáveis request.auth
e resource.data
para restringir o acesso de leitura e gravação de cada story
ao respectivo autor:
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;
}
}
}
Suponha que o aplicativo inclua uma página que mostra ao usuário uma lista de documentos story
de autoria própria. Você pode pensar que é possível usar a seguinte consulta para preencher esta página. No entanto, a consulta falhará, porque ela não inclui as mesmas restrições que suas regras de segurança:
Inválido: as restrições da consulta não correspondem às das regras de segurança.
// This query will fail
db.collection("stories").get()
A consulta falhará mesmo se o usuário atual realmente for o autor de todos os documentos story
. O motivo desse comportamento é que, quando
o Firestore aplica suas regras de segurança, ele avalia a consulta
em relação ao conjunto de resultados em potencial, não às propriedades reais dos
documentos no banco de dados. Se uma consulta puder potencialmente incluir documentos que violam as regras de segurança, ela falhará.
Em contraste, a seguinte consulta é bem-sucedida, porque inclui a mesma restrição no campo author
que as regras de segurança:
Válido: as restrições da consulta correspondem às restrições das regras de segurança.
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
Proteger e consultar documentos com base em um campo
Para demonstrar melhor a interação entre consultas e regras, as regras de segurança abaixo expandem o acesso de leitura para a coleção de stories
para permitir que qualquer usuário leia documentos de story
quando o campo published
estiver definido como 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;
}
}
}
A consulta a páginas publicadas precisa incluir as mesmas restrições que as regras de segurança:
db.collection("stories").where("published", "==", true).get()
A restrição de consulta .where("published", "==", true)
garante que resource.data.published
é true
para qualquer resultado. Portanto, essa consulta
satisfaz as regras de segurança e pode ler os dados.
OR
consultas
Ao avaliar uma consulta OR
lógica (or
, in
ou array-contains-any
)
em relação a um conjunto de regras, o Firestore avalia cada valor de comparação
de forma separada. Cada valor de comparação precisa obedecer às restrições da regra de segurança. Por
exemplo, para a
regra a seguir:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
Inválido: a consulta não garante que
x > 5
para todos os documentos em potencial.
// 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])
)
Válido: a consulta garante que x > 5
para todos os documentos em potencial.
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
Como avaliar restrições em consultas
Suas regras de segurança também podem aceitar ou negar consultas com base nas próprias restrições.
A variável request.query
contém as propriedades limit
, offset
e orderBy
de uma consulta. Por exemplo, suas regras de segurança podem negar qualquer consulta que não limite o número máximo de documentos recuperados a um determinado intervalo:
allow list: if request.query.limit <= 10;
O conjunto de regras a seguir demonstra como escrever regras de segurança que avaliam restrições incluídas em consultas. Este exemplo expande o conjunto de regras stories
anterior com as seguintes alterações:
- O conjunto de regras separa a regra de leitura em regras
get
elist
. - A regra
get
restringe a recuperação de documentos únicos a documentos públicos ou documentos de autoria do usuário. - O
list
aplica as mesmas restrições queget
, mas para consultas. Ela também verifica o limite da consulta e nega qualquer consulta sem limite ou com um limite superior a 10. - O conjunto de regras define uma função
authorOrPublished()
para evitar a duplicação de código.
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;
}
}
}
Regras de segurança e consultas do grupo de coleções
Por padrão, as consultas têm escopo delimitado a uma única coleção e recuperam apenas os resultados dela. Com as consultas do grupo de coleções, é possível recuperar os resultados de um grupo que inclui todas as coleções com o mesmo ID. Esta seção descreve como usar regras de segurança para proteger suas consultas do grupo de coleções.
Proteger e consultar documentos com base em grupos de coleções
Nas suas regras de segurança, você precisa permitir explicitamente as consultas do grupo de coleções. Para isso, grave uma regra para esse grupo:
- Verifique se
rules_version = '2';
é a primeira linha do seu conjunto de regras. As consultas do grupo de coleções exigem o comportamento do novo caractere curinga recursivo{name=**}
da versão 2 das regras de segurança. - Escreva uma regra para seu grupo de coleções usando
match /{path=**}/[COLLECTION_ID]/{doc}
.
Por exemplo, pense em um fórum organizado em documentos forum
que contenham subcoleções posts
:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
Nesse aplicativo, tornamos as postagens editáveis pelos proprietários e legíveis para os usuários autenticados:
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;
}
}
}
Todos os usuários autenticados podem recuperar as postagens de qualquer fórum:
db.collection("forums/technology/posts").get()
E se você quiser mostrar ao usuário atual as postagens dele em todos os fóruns?
Para fazer isso, use uma consulta do grupo de coleções para recuperar os resultados de todas as coleções posts
:
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
Nas suas regras de segurança, é preciso permitir essa consulta. Para fazer isso, escreva uma regra de leitura ou de lista para o grupo de coleções 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;
}
}
}
Essas regras serão aplicadas a todas as coleções com o ID posts
, independentemente da hierarquia. Por exemplo, elas serão aplicadas a todas as seguintes coleções posts
:
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
Proteger consultas do grupo de coleções com base em um campo
Assim como as consultas de uma única coleção, as consultas do grupo de coleções também precisam atender às restrições definidas nas suas regras de segurança. Por exemplo, podemos adicionar um campo published
a cada postagem do fórum, como fizemos no exemplo stories
acima:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
Nesse momento, podemos escrever regras para o grupo de coleções posts
com base no status
published
e na postagem 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;
}
}
}
Com essas regras, os clientes da Web, Apple e Android podem fazer as seguintes consultas:
Qualquer pessoa pode recuperar postagens publicadas em um fórum:
db.collection("forums/technology/posts").where('published', '==', true).get()
Qualquer pessoa pode recuperar as postagens publicadas por um autor em todos os fóruns:
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
Os autores podem recuperar todas as postagens que publicaram e cancelaram a publicação em todos os fóruns:
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
Proteger e consultar documentos com base no grupo de coleções e no caminho do documento
Em alguns casos, é recomendado restringir as consultas do grupo de coleções com base no caminho do documento. Se quiser criar essas restrições, use as mesmas técnicas adotadas para proteger e consultar documentos com base em um campo.
Pense em um aplicativo que rastreia as transações de cada usuário em várias criptomoedas e bolsas de valores:
/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",
}
Observe o campo user
. Sabemos a que usuário pertence um documento transaction
do caminho de documento, mas duplicamos essas informações em cada documento transaction
porque assim podemos realizar duas ações:
Gravar consultas do grupo de coleções restritas a documentos que incluam um
/users/{userid}
específico no caminho do documento. Exemplo: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)
Aplicar essa restrição a todas as consultas no grupo de coleções
transactions
para que um usuário não consiga recuperar documentostransaction
de outro usuário.
Aplicamos essa restrição nas nossas regras de segurança e incluímos validação de dados para o campo 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
}
}
}
Próximas etapas
- Para ver um exemplo mais detalhado do controle de acesso baseado em papéis, consulte Como proteger o acesso a dados para usuários e grupos.
- Leia a referência de regras de segurança.