Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Gerenciar e implantar regras de segurança do Firebase

O Firebase fornece várias ferramentas para gerenciar suas regras. Cada uma é útil em casos específicos, e cada uma usa a mesma API de gerenciamento de regras de segurança do Firebase.

Seja qual for a ferramenta usada para invocá-la, a API de gerenciamento:

  • ingere uma origem de regras: um conjunto de regras, normalmente um arquivo de código contendo instruções de regras de segurança do Firebase;
  • armazena a origem ingerida como um conjunto de regras imutável;
  • monitora a implantação de cada conjunto de regras em uma versão. Os serviços ativados para regras de segurança do Firebase pesquisam a versão de um projeto para avaliar cada solicitação de um recurso protegido;
  • permite executar testes sintáticos e semânticos em um conjunto de regras.

Usar a CLI do Firebase

Com a CLI do Firebase, é possível fazer upload de origens locais e implantar versões. Com o Pacote do emulador local do Firebase, você pode realizar testes locais completos de origens.

O uso da CLI permite que você mantenha suas regras com controle de versões com o código do aplicativo e implemente regras como parte do processo de implantação existente.

Gerar um arquivo de configuração

Ao configurar o projeto do Firebase usando a Firebase CLI, você cria um arquivo de configuração .rules no diretório do projeto. Use o seguinte comando para começar a configurar o projeto do Firebase:

Cloud Firestore

// Set up Firestore in your project directory, creates a .rules file
firebase init firestore

Realtime Database

// Set up Realtime Database in your project directory, creates a .rules file
firebase init database

Cloud Storage

// Set up Storage in your project directory, creates a .rules file
firebase init storage

Editar e atualizar as regras

Edite sua origem de regras diretamente no arquivo de configuração .rules. Certifique-se de que todas as edições feitas na CLI do Firebase sejam refletidas no Console do Firebase ou que você faça atualizações constantemente usando o Console do Firebase ou a CLI do Firebase. Caso contrário, será possível substituir as atualizações feitas no Console do Firebase.

Testar as atualizações

O Pacote do emulador local fornece emuladores para todos os produtos compatíveis com regras de segurança. O mecanismo de regras de segurança de cada emulador executa a avaliação sintática e semântica das regras, excedendo o teste sintático oferecido pela API de gerenciamento de regras de segurança.

Se você estiver trabalhando com a CLI, o pacote será uma excelente ferramenta para testes de regras de segurança do Firebase. Use o Pacote do emulador local para testar as atualizações localmente e verificar se as regras do aplicativo exibem o comportamento desejado.

Implantar as atualizações

Depois de atualizar e testar as regras, implante as origens na produção. Use os comandos a seguir para implantá-las seletivamente ou como parte do processo normal de implantação.

Cloud Firestore

// Deploy your .rules file
firebase deploy --only firestore:rules

Realtime Database

// Deploy your .rules file
firebase deploy --only database

Cloud Storage

// Deploy your .rules file
firebase deploy --only storage

Usar o Console do Firebase

Também é possível editar origens de regras e implantá-las como versões usando o Console do Firebase. Os testes sintáticos são realizados à medida que você edita na IU do Console do Firebase e os testes semânticos estão disponíveis pelo Laboratório de testes de regras.

Editar e atualizar as regras

  1. Abra o Console do Firebase e selecione o projeto.
  2. Em seguida, selecione Realtime Database, Cloud Firestore ou Storage na navegação do produto e clique em Regras para acessar o Editor de regras.
  3. Altere as regras diretamente no editor.

Testar as atualizações

Além de testar a sintaxe na IU do editor, é possível testar o comportamento semântico das regras usando os recursos de armazenamento e banco de dados do seu projeto, diretamente no Console do Firebase, pelo Laboratório de testes de regras. Abra a tela do Laboratório de testes de regras no Editor de regras, modifique as configurações e clique em Executar. Procure a mensagem de confirmação na parte superior do editor.

Implantar as atualizações

Quando você achar que as atualizações foram implementadas conforme o esperado, clique em Publicar.

Usar o SDK Admin

É possível usar o SDK Admin para conjuntos de regras do Node.js. Com esse acesso programático, é possível:

  • Implementar ferramentas, scripts, painéis e pipelines de CI/CD personalizados para gerenciar regras.
  • Gerenciar regras com mais facilidade em vários projetos do Firebase.

Ao atualizar regras de maneira programática, é muito importante evitar alterações indesejadas no controle de acesso do seu aplicativo. Escreva seu código do SDK Admin tendo em mente a segurança, principalmente ao atualizar ou implantar regras.

Outra coisa importante a ser lembrada é que as versões de regras de segurança do Firebase levam alguns minutos para serem propagadas por completo. Ao usar o SDK Admin para implantar regras, evite disputas em que o aplicativo dependa imediatamente de regras cuja implantação ainda não foi concluída. Se o caso de uso exigir atualizações frequentes para regras de controle de acesso, considere soluções que usam o Cloud Firestore, que foi projetado para reduzir disputas, apesar das atualizações frequentes.

Observe também estes limites:

  • As regras precisam ser menores que 256 KiB de texto codificado em UTF-8 quando serializadas.
  • Um projeto pode ter no máximo 2.500 conjuntos de regras implantados. Quando esse limite for atingido, você precisará excluir alguns conjuntos de regras antigos antes de criar novos.

Criar e implantar conjuntos de regras do Cloud Storage ou do Cloud Firestore

Um fluxo de trabalho comum para gerenciar regras de segurança com o SDK Admin pode incluir três etapas distintas:

  1. Criar uma origem de arquivo de regras (opcional)
  2. Criar um conjunto de regras
  3. Libere ou implante o novo conjunto de regras

O SDK fornece um método para combinar essas etapas em uma única chamada de API para regras de segurança do Cloud Storage e do Cloud Firestore. Exemplo:

    const source = `service cloud.firestore {
      match /databases/{database}/documents {
        match /carts/{cartID} {
          allow create: if request.auth != null && request.auth.uid == request.resource.data.ownerUID;
          allow read, update, delete: if request.auth != null && request.auth.uid == resource.data.ownerUID;
        }
      }
    }`;
    // Alternatively, load rules from a file
    // const fs = require('fs');
    // const source = fs.readFileSync('path/to/firestore.rules', 'utf8');

    await admin.securityRules().releaseFirestoreRulesetFromSource(source);

Esse mesmo padrão funciona para regras do Cloud Storage com releaseFirestoreRulesetFromSource().

Como alternativa, é possível criar o arquivo de regras como um objeto na memória, criar o conjunto de regras e implantá-lo separadamente para ter um controle mais próximo desses eventos. Exemplo:

    const rf = admin.securityRules().createRulesFileFromSource('firestore.rules', source);
    const rs = await admin.securityRules().createRuleset(rf);
    await admin.securityRules().releaseFirestoreRuleset(rs);

Atualizar conjuntos de regras do Realtime Database

Para atualizar os conjuntos de regras do Realtime Database com o SDK Admin, use os métodos getRules() e setRules() de admin.database. É possível recuperar conjuntos de regras no formato JSON ou como uma string com comentários incluídos.

Para atualizar um conjunto de regras:

    const source = `{
      "rules": {
        "scores": {
          ".indexOn": "score",
          "$uid": {
            ".read": "$uid == auth.uid",
            ".write": "$uid == auth.uid"
          }
        }
      }
    }`;
    await admin.database().setRules(source);

Gerenciar conjuntos de regras

Para ajudar a gerenciar grandes conjuntos de regras, o SDK Admin permite listar todas as regras existentes com admin.securityRules().listRulesetMetadata. Exemplo:

    const allRulesets = [];
    let pageToken = null;
    while (true) {
      const result = await admin.securityRules().listRulesetMetadata(pageToken: pageToken);
      allRulesets.push(...result.rulesets);
      pageToken = result.nextPageToken;
      if (!pageToken) {
        break;
      }
    }

Para implantações muito grandes que atingem o limite de 2.500 regras ao longo do tempo, é possível criar uma lógica para excluir as regras mais antigas em um ciclo de tempo fixo. Por exemplo, para excluir todos os conjuntos de regras implantados por mais de 30 dias:

    const thirtyDays = new Date(Date.now() - THIRTY_DAYS_IN_MILLIS);
    const promises = [];
    allRulesets.forEach((rs) => {
      if (new Date(rs.createTime) < thirtyDays) {
        promises.push(admin.securityRules().deleteRuleset(rs.name));
      }
    });
    await Promise.all(promises);
    console.log(`Deleted ${promises.length} rulesets.`);

Usar a API REST

As ferramentas descritas acima são adequadas para vários fluxos de trabalho, mas talvez você queira gerenciar e implantar as regras de segurança do Firebase usando a própria API de gerenciamento. A API de gerenciamento oferece a maior flexibilidade.

As versões das regras de segurança do Firebase levam algum tempo para serem totalmente propagadas. Ao usar a API REST de gerenciamento para implantar, evite as disputas em que seu aplicativo dependa imediatamente das regras cuja implantação ainda não esteja concluída.

Observe também estes limites:

  • As regras precisam ser menores que 256 KiB de texto codificado em UTF-8 quando serializadas.
  • Um projeto pode ter no máximo 2.500 conjuntos de regras implantados. Quando esse limite for atingido, você precisará excluir alguns conjuntos de regras antigos antes de criar novos.

Criar e implantar conjuntos de regras do Cloud Storage ou do Cloud Firestore com REST

Os exemplos nesta seção usam regras de armazenamento, mas elas também se aplicam às regras do Cloud Firestore.

Os exemplos também usam cURL para fazer chamadas de API. As etapas para configurar e transmitir tokens de autenticação são omitidas. Teste essa API usando o API Explorer integrado com a documentação de referência.

As etapas típicas para criar e implantar um conjunto de regras usando a API de gerenciamento são as seguintes:

  1. Criar origens de arquivos de regras
  2. Criar um conjunto de regras
  3. Lançar (implantar) o novo conjunto de regras.

Vamos supor que você esteja trabalhando no seu projeto do Firebase secure_commerce e queira implantar as regras do Cloud Storage bloqueadas. É possível implementar essas regras em um arquivo storage.rules.

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}

Agora, gere uma impressão digital codificada em base64 para esse arquivo. Em seguida, use a origem nesse arquivo para preencher o payload necessário para criar um conjunto de regras com a chamada REST projects.rulesets.create. Aqui, usamos o comando cat para inserir o conteúdo de storage.rules no payload REST.

curl -X POST -d '{
  "source": {
    {
      "files": [
        {
          "content": "' $(cat storage.rules) '",
          "name": "storage.rules",
          "fingerprint": <sha fingerprint>
        }
      ]
    }
  }
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/rulesets'

A API retorna uma resposta de validação e um nome de conjunto de regras, por exemplo, projects/secure_commerce/rulesets/uuid123. Se o conjunto de regras for válido, a etapa final será implantar o novo conjunto em uma versão nomeada.

curl -X POST -d '{
  "name": "projects/secure_commerce/releases/prod/v23   "  ,
  "rulesetName": "projects/secure_commerce/rulesets/uuid123",
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/releases'

Atualizar conjuntos de regras do Realtime Database com REST

O Realtime Database oferece a própria interface REST para gerenciar regras. Consulte Como gerenciar regras do Firebase Realtime Database usando a API REST.

Gerenciar conjuntos de regras com REST

Para ajudar a gerenciar implantações de regras grandes, além de um método REST para criar conjuntos de regras e versões, a API de gerenciamento fornece métodos para:

  • listar, recuperar e excluir conjuntos de regras
  • listar, receber e excluir versões de regras

Para implantações muito grandes que atingem o limite de 2.500 regras ao longo do tempo, é possível criar uma lógica para excluir as regras mais antigas em um ciclo de tempo fixo. Por exemplo, para excluir todos os conjuntos de regras implantados por mais de 30 dias, chame o método projects.rulesets.list, analise a lista JSON de objetos Ruleset nas chaves createTime e chame project.rulesets.delete nos conjuntos de regras correspondentes por ruleset_id.

Testar as atualizações com REST

Por fim, a API de gerenciamento permite executar testes sintáticos e semânticos nos recursos do Cloud Firestore e do Cloud Storage nos projetos de produção.

O teste com este componente da API consiste em:

  1. Definir um objeto JSON TestSuite para representar um conjunto de objetos TestCase
  2. Enviar o TestSuite
  3. Analisar os objetos TestResult retornados

Vamos definir um objeto TestSuite com um único TestCase em um arquivo testcase.json. Neste exemplo, enviamos a origem da linguagem de regras in-line com o payload REST, junto com o conjunto de testes para executar essas regras. Especificamos uma expectativa de avaliação de regras e a solicitação do cliente que será usada para testar o conjunto de regras. Também é possível especificar quão completo será o relatório de teste, usando o valor "FULL" para indicar os resultados de todas as expressões de linguagem das regras que devem ser incluídas no relatório, incluindo expressões que não corresponderam à solicitação.

 {
  "source":
  {
    "files":
    [
      {
        "name": "firestore.rules",
        "content": "service cloud.firestore {
          match /databases/{database}/documents {
            match /users/{userId}{
              allow read: if (request.auth.uid == userId);
            }
            function doc(subpath) {
              return get(/databases/$(database)/documents/$(subpath)).data;
            }
            function isAccountOwner(accountId) {
              return request.auth.uid == accountId
                  || doc(/users/$(request.auth.uid)).accountId == accountId;
            }
            match /licenses/{accountId} {
              allow read: if isAccountOwner(accountId);
            }
          }
        }"
      }
    ]
  },
  "testSuite":
  {
    "testCases":
    [
      {
        "expectation": "ALLOW",
        "request": {
           "auth": {"uid": "123"},
           "path": "/databases/(default)/documents/licenses/abcd",
           "method": "get"},
        "functionMocks": [
            {
            "function": "get",
            "args": [{"exact_value": "/databases/(default)/documents/users/123"}],
            "result": {"value": {"data": {"accountId": "abcd"}}}
            }
          ]
      }
    ]
  }
}

Podemos enviar este TestSuite para avaliação com o método projects.test.

curl -X POST -d '{
    ' $(cat testcase.json) '
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/rulesets/uuid123:test'

O TestReport retornado (contendo o status de teste SUCCESS/FAILURE, listas de mensagens de depuração, listas de expressões de regras visitadas e os relatórios de avaliação) confirmaria com o status SUCCESS que o acesso foi permitido corretamente.