Proteger os dados do usuário

As Firebase Security Rules para Cloud Storage integram-se ao Firebase Authentication para fornecer autenticação avançada com base no usuário para o Cloud Storage. Isso permite o controle de acesso granular com base nas declarações de um token do Firebase Authentication.

Autenticação do usuário

Quando um usuário autenticado faz uma solicitação no Cloud Storage, a variável request.auth é preenchida com o uid do usuário (request.auth.uid), bem como as declarações do JWT Firebase Authentication (request.auth.token).

Além disso, ao usar a autenticação personalizada, outras declarações são exibidas no campo request.auth.token.

Quando um usuário não autenticado realiza uma solicitação, a variável request.auth é null.

Com esses dados, há várias maneiras comuns de usar a autenticação para proteger os arquivos:

  • Público: ignora request.auth
  • Particular autenticado: verifica se request.auth não é null
  • Particular por usuário: verifica se request.auth.uid é igual a um caminho uid
  • Particular por grupo: verifica as declarações do token personalizado para confirmar se elas correspondem a uma declaração escolhida ou lê os metadados do arquivo para ver se existe um campo de metadados

Público

Qualquer regra que não considere o contexto request.auth pode ser considerada uma regra public, pois não considera o contexto de autenticação do usuário. Essas regras podem ser úteis para expor dados públicos, como recursos de um jogo, arquivos de som ou outros conteúdos estáticos.

// Anyone to read a public image if the file is less than 100kB
// Anyone can upload a public file ending in '.txt'
match /public/{imageId} {
  allow read: if resource.size < 100 * 1024;
  allow write: if imageId.matches(".*\\.txt");
}

Particular autenticado

Em determinados casos, os dados devem ser visíveis somente para os usuários autenticados do seu aplicativo. Como a variável request.auth é null para todos os usuários não autenticados, basta verificar se a variável request.auth existe para exigir autenticação:

// Require authentication on all internal image reads
match /internal/{imageId} {
  allow read: if request.auth != null;
}

Particular por usuário

De longe, o caso de uso mais comum para request.auth será conceder permissões granulares aos arquivos para cada usuário, desde o upload de imagens de perfil até a leitura de documentos particulares.

Como os arquivos do Cloud Storage têm um caminho completo até o arquivo, para permitir que um usuário controle um arquivo, basta uma informação exclusiva que identifique o usuário no caminho (como o uid do usuário). Essa informação pode ser verificada quando a regra é avaliada:

// Only a user can upload their profile picture, but anyone can view it
match /users/{userId}/profilePicture.png {
  allow read;
  allow write: if request.auth != null && request.auth.uid == userId;
}

Particular por grupo

Outro caso de uso igualmente comum é conceder permissões de acesso a um objeto para um grupo. Por exemplo, permitir que vários membros da equipe colaborem em um documento compartilhado. Há várias abordagens para fazer isso, como:

  • produzir um token personalizado do Firebase Authentication que contenha informações adicionais sobre um membro do grupo (por exemplo, um ID do grupo);
  • incluir informações do grupo (como um ID do grupo ou uma lista de uids autorizados) nos metadados do arquivo.

Depois que esses dados são armazenados no token ou nos metadados do arquivo, eles podem ser referenciados dentro de uma regra:

// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
  allow read: if resource.metadata.owner == request.auth.token.groupId;
  allow write: if request.auth.token.groupId == groupId;
}

Exemplo completo

Veja no exemplo abaixo casos simples dos quatro tipos comuns de restrições de autenticação:

service firebase.storage {
  match /b/{bucket}/o {
    match /images {
      // Anyone can view any image (no auth, publicly readable)
      match /{allImages=**} {
        allow read;
      }

      // Only authenticated users can write to "public" images
      match /public/{imageId} {
        allow write: if request.auth != null;
      }

      // Only an individual user can write to "their" images
      match /{userId}/{imageId} {
        allow write: if request.auth.uid == userId;
      }

      // Allow a "group" of users to read/write to shared images
      // An owner metadata property on the object contains the groupId for reads
      // A custom token has been minted with a groupId property for writes
      match /{groupId}/{imageId} {
        allow read: if resource.metadata.owner == request.auth.token.groupId;
        allow write: if request.auth.token.groupId == groupId;
      }
    }
  }
}