Consultar dados em segurança
Esta página baseia-se nos conceitos em
Estruturar regras de segurança e
Escrever condições para regras de segurança para explicar como as
regras de segurança do Firestore interagem com as consultas. Analisa mais detalhadamente a forma como as regras de segurança afetam as consultas que pode escrever e descreve como garantir que as consultas usam as mesmas restrições que as regras de segurança. Esta página também descreve como escrever regras de segurança para permitir ou negar consultas com base em propriedades de consulta, como limit
e orderBy
.
As regras não são filtros
Quando escrever consultas para obter documentos, tenha em atenção que as regras de segurança não são filtros. As consultas são tudo ou nada. Para poupar tempo e recursos, o Firestore avalia uma consulta em relação ao respetivo conjunto de resultados potencial em vez dos valores de campo reais de todos os seus documentos. Se uma consulta puder devolver documentos que o cliente não tem autorização para ler, todo o pedido falha.
Consultas e regras de segurança
Conforme demonstrado nos exemplos abaixo, tem de escrever as suas consultas de forma a ajustarem-se às restrições das suas regras de segurança.
Proteja e consulte documentos com base em auth.uid
O exemplo seguinte demonstra como escrever uma consulta para obter documentos protegidos por uma regra de segurança. Considere uma base de dados que contém uma coleção de
story
documentos:
/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 controlo de acesso. Estes exemplos pressupõem que a app usa a Firebase Authentication para definir o campo author
para o UID do utilizador que criou o documento. O Firebase
Authentication também preenche a variável request.auth
nas
regras de segurança.
A seguinte regra de segurança usa as variáveis request.auth
e
resource.data
para restringir o acesso de leitura e escrita de cada
story
ao respetivo 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;
}
}
}
Suponhamos que a sua app inclui uma página que mostra ao utilizador uma lista de story
documentos que criou. Pode esperar usar a seguinte consulta para preencher esta página. No entanto, esta consulta vai falhar porque não inclui as mesmas restrições que as suas regras de segurança:
Inválido: as restrições de consulta não correspondem às restrições das regras de segurança
// This query will fail
db.collection("stories").get()
A consulta falha mesmo que o utilizador atual seja efetivamente o autor de todos os documentos story
. A razão para este comportamento é que, quando o Firestore aplica as suas regras de segurança, avalia a consulta em função do conjunto de resultados potenciais e não das propriedades reais dos documentos na sua base de dados. Se uma consulta puder potencialmente incluir documentos que violem as suas regras de segurança, a consulta falha.
Por outro lado, 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 de consulta correspondem às restrições das regras de segurança
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
Proteja e consulte documentos com base num campo
Para demonstrar ainda mais a interação entre consultas e regras, as regras de segurança abaixo expandem o acesso de leitura para a coleção stories
para permitir que qualquer utilizador leia documentos story
onde o campo published
está 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 de páginas publicadas tem de 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. Por conseguinte, esta consulta
satisfaz as regras de segurança e tem autorização para ler dados.
OR
consultas
Quando avalia uma consulta lógica (or
, in
ou array-contains-any
) em relação a um conjunto de regras, o Firestore avalia cada valor de comparação separadamente.OR
Cada valor de comparação tem de cumprir as restrições da regra de segurança. Por
exemplo, para a
seguinte regra:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
Inválido: a consulta não garante que
x > 5
para todos os documentos potenciais
// 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 potenciais
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
Avaliação das restrições nas consultas
As regras de segurança também podem aceitar ou recusar consultas com base nas respetivas restrições.
A variável request.query
contém as propriedades limit
, offset
e orderBy
de uma consulta. Por exemplo, as suas regras de segurança podem recusar qualquer consulta que não limite o número máximo de documentos obtidos a um determinado intervalo:
allow list: if request.query.limit <= 10;
O conjunto de regras seguinte demonstra como escrever regras de segurança que avaliam as restrições aplicadas às consultas. Este exemplo expande o conjunto de regras anterior com as seguintes alterações:stories
- O conjunto de regras separa a regra de leitura em regras para
get
elist
. - A regra
get
restringe a obtenção de documentos individuais a documentos públicos ou documentos criados pelo utilizador. - A regra
list
aplica as mesmas restrições queget
, mas para consultas. Também verifica o limite de consultas e, em seguida, rejeita qualquer consulta sem um 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;
}
}
}
Consultas e regras de segurança do grupo de recolha
Por predefinição, as consultas são limitadas a uma única coleção e obtêm resultados apenas dessa coleção. Com as consultas de grupos de coleções, pode obter resultados de um grupo de coleções composto por todas as coleções com o mesmo ID. Esta secção descreve como proteger as consultas de grupos de recolha através de regras de segurança.
Proteja e consulte documentos com base em grupos de coleções
Nas regras de segurança, tem de permitir explicitamente as consultas de grupos de recolha escrevendo uma regra para o grupo de recolha:
- Certifique-se de que
rules_version = '2';
é a primeira linha do conjunto de regras. As consultas de grupos de coleções requerem o comportamento novo de carateres universais recursivos{name=**}
da versão 2 das regras de segurança. - Escreva uma regra para o seu grupo de recolha através do
match /{path=**}/[COLLECTION_ID]/{doc}
.
Por exemplo, considere um fórum organizado em forum
documentos que contêm
posts
subcoleções:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
Nesta aplicação, tornamos as publicações editáveis pelos respetivos proprietários e legíveis por utilizadores 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;
}
}
}
Qualquer utilizador autenticado pode obter as publicações de qualquer fórum:
db.collection("forums/technology/posts").get()
Mas e se quiser mostrar ao utilizador atual as respetivas publicações em todos os fóruns?
Pode usar uma consulta de grupo de coleções para obter resultados de todas as coleções posts
:
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
Nas regras de segurança, tem de permitir esta consulta escrevendo 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;
}
}
}
No entanto, tenha em atenção que estas regras aplicam-se a todas as coleções com o ID posts
, independentemente da hierarquia. Por exemplo, estas regras aplicam-se a todas as seguintes coleções:posts
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
Proteja as consultas de grupos de recolha com base num campo
Tal como as consultas de recolha única, as consultas de grupos de recolhas também têm de cumprir as restrições definidas pelas suas regras de segurança. Por exemplo, podemos adicionar um campo published
a cada publicação no fórum, tal 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
}
Em seguida, podemos escrever regras para o grupo de recolha posts
com base no estado published
e na publicação 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 estas regras, os clientes Web, Apple e Android podem fazer as seguintes consultas:
Qualquer pessoa pode aceder a publicações publicadas num fórum:
db.collection("forums/technology/posts").where('published', '==', true).get()
Qualquer pessoa pode aceder às publicações publicadas de um autor em todos os fóruns:
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
Os autores podem aceder a todas as suas publicações publicadas e não publicadas em todos os fóruns:
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
Proteja e consulte documentos com base no grupo de coleções e no caminho do documento
Em alguns casos, pode querer restringir as consultas de grupos de recolha com base no caminho do documento. Para criar estas restrições, pode usar as mesmas técnicas para proteger e consultar documentos com base num campo.
Considere uma aplicação que acompanha as transações de cada utilizador entre várias bolsas de valores e criptomoedas:
/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",
}
Repare no campo user
. Embora saibamos que utilizador é proprietário de um transaction
documento a partir do caminho do documento, duplicamos estas informações em cada documento transaction
porque nos permite fazer duas coisas:
Escrever consultas de grupos de coleções restritas a documentos que incluam um
/users/{userid}
específico no respetivo caminho do documento. Por 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)
Aplique esta restrição a todas as consultas no grupo de recolha
transactions
para que um utilizador não possa obter os documentostransaction
de outro utilizador.
Aplicamos esta restrição nas nossas regras de segurança e incluímos a 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
}
}
}
Passos seguintes
- Para um exemplo mais detalhado do controlo de acesso baseado em funções, consulte o artigo Proteger o acesso aos dados para utilizadores e grupos.
- Leia a referência das regras de segurança.