Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Consultar dados com segurança

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Esta página se baseia nos conceitos de Estruturação de regras de segurança e Gravação de condições para regras de segurança para explicar como as regras de segurança do Cloud Firestore interagem com as consultas. Ele examina mais de perto como as regras de segurança afetam as consultas que você pode escrever e descreve como garantir que suas consultas usem as mesmas restrições que suas 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

Ao escrever consultas para recuperar documentos, lembre-se de que as regras de segurança não são filtros — as consultas são tudo ou nada. Para economizar tempo e recursos, o Cloud Firestore avalia uma consulta em relação ao conjunto de resultados em potencial, em vez dos valores de campo reais de todos os seus documentos. Se uma consulta puder retornar documentos que o cliente não tem permissão para ler, toda a solicitação falhará.

Consultas e regras de segurança

Como os exemplos abaixo demonstram, você deve escrever suas consultas para atender às restrições de suas regras de segurança.

Proteja e consulte documentos com base em auth.uid

O exemplo a seguir demonstra 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 de story :

/stories/{storyid}

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

Além dos campos de title e content , cada documento armazena o author e os campos published para uso no controle de acesso. Esses exemplos pressupõem que o aplicativo usa o Firebase Authentication para definir o campo de 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.

A seguinte regra de segurança usa as variáveis request.auth e resource.data para restringir o acesso de leitura e gravação de cada story ao seu 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 seu aplicativo inclua uma página que mostre ao usuário uma lista de documentos de story de sua autoria. Você pode esperar usar a consulta a seguir para preencher esta página. No entanto, essa consulta falhará porque não inclui as mesmas restrições que 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 se o usuário atual for o autor de cada documento da story . O motivo desse comportamento é que, quando o Cloud Firestore aplica suas regras de segurança, ele avalia a consulta em relação ao conjunto de resultados em potencial , não em relação às propriedades reais dos documentos em seu banco de dados. Se uma consulta puder incluir documentos que violem suas regras de segurança, a consulta falhará.

Por outro lado, a consulta a seguir é bem-sucedida porque inclui a mesma restrição no campo de 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 em um 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 de stories para permitir que qualquer usuário leia documentos de story em que o campo published esteja 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 deve incluir as mesmas restrições das regras de segurança:

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

A restrição de consulta .where("published", "==", true) garante que resource.data.published seja true para qualquer resultado. Portanto, essa consulta atende às regras de segurança e tem permissão para ler dados.

in e array-contains-any consulta

Ao avaliar uma cláusula de consulta in ou array-contains-any em um conjunto de regras, o Cloud Firestore avalia cada valor de comparação separadamente. Cada valor de comparação deve atender às 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 em potencial

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

Válido : A consulta garante que x > 5 para todos os documentos potenciais

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

Avaliando restrições em consultas

Suas regras de segurança também podem aceitar ou negar consultas com base em suas 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 colocadas em consultas. Este exemplo expande o conjunto de regras de stories anteriores com as seguintes alterações:

  • O conjunto de regras separa a regra de leitura em regras para get e list .
  • A regra get restringe a recuperação de documentos únicos a documentos públicos ou documentos de autoria do usuário.
  • A regra de list aplica as mesmas restrições que get , mas para consultas. Ele também verifica o limite de consulta e nega qualquer consulta sem limite ou com limite maior que 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 de grupo de coleção e regras de segurança

Por padrão, as consultas têm como escopo uma única coleção e recuperam os resultados somente dessa coleção. Com consultas de grupo de coleção , você pode recuperar resultados de um grupo de coleção que consiste em todas as coleções com o mesmo ID. Esta seção descreve como proteger suas consultas de grupo de coleta usando regras de segurança.

Proteja e consulte documentos com base em grupos de coleção

Em suas regras de segurança, você deve permitir explicitamente consultas de grupo de coleta escrevendo uma regra para o grupo de coleta:

  1. Certifique-se rules_version = '2'; é a primeira linha do seu conjunto de regras. As consultas de grupo de coleção exigem o novo comportamento curinga recursivo {name=**} das regras de segurança versão 2.
  2. Escreva uma regra para seu grupo de coleção usando match /{path=**}/ [COLLECTION_ID] /{doc} .

Por exemplo, considere um fórum organizado em documentos do forum contendo subcoleções de posts :

/forums/{forumid}/posts/{postid}

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

Neste aplicativo, fazemos postagens editáveis ​​por seus proprietários e legíveis por 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;
    }
  }
}

Qualquer usuário autenticado pode recuperar as postagens de qualquer fórum:

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

Mas e se você quiser mostrar ao usuário atual suas postagens em todos os fóruns? Você pode usar uma consulta de grupo de coleção para recuperar resultados de todas as coleções de posts :

var user = firebase.auth().currentUser;

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

Nas suas regras de segurança, você deve permitir essa consulta escrevendo uma regra de leitura ou lista para o grupo de coleta de 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;

    }
  }
}

Observe, no entanto, que essas regras serão aplicadas a todas as coleções com posts de ID , independentemente da hierarquia. Por exemplo, essas regras se aplicam a todas as seguintes coleções de posts :

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

Consultas seguras do grupo de coleta com base em um campo

Assim como as consultas de coleção única, as consultas de grupo de coleção também devem atender às restrições definidas por suas regras de segurança. Por exemplo, podemos adicionar um campo published a cada postagem do fórum, como fizemos no exemplo de stories acima:

/forums/{forumid}/posts/{postid}

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

Podemos então escrever regras para o grupo de coleta de posts com base no status published e no author da postagem:

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 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 de 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 suas postagens 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 coleta e no caminho do documento

Em alguns casos, você pode querer restringir as consultas do grupo de coleções com base no caminho do documento. Para criar essas restrições, você pode usar as mesmas técnicas para proteger e consultar documentos com base em um campo.

Considere um aplicativo que acompanha as transações de cada usuário 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",
}

Observe o campo de user . Embora saibamos qual usuário possui um documento de transaction a partir do caminho do documento, duplicamos essa informação em cada documento de transaction porque isso nos permite fazer duas coisas:

  • Grave consultas de grupo de coleção restritas a documentos que incluem um /users/{userid} específico em seu caminho de 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)
    
  • Imponha essa restrição para todas as consultas no grupo de coleta de transactions para que um usuário não possa recuperar os documentos de transaction de outro usuário.

Aplicamos essa restrição em nossas regras de segurança e incluímos validação de dados para o campo de 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óximos passos