Referência de regras de segurança do Firebase para Cloud Storage

As regras de segurança do Firebase para Cloud Storage são usadas para determinar quem tem acesso de leitura e gravação aos arquivos armazenados no Cloud Storage, bem como como os arquivos são estruturados e quais metadados eles contêm. As regras de segurança do Cloud Storage são compostas por regras que consideram a request e resource para permitir ou negar uma ação desejada, como fazer upload de um arquivo ou recuperar metadados de arquivo. Esses documentos de referência abrangem os tipos de regras, as propriedades de uma request e de um resource , os tipos de dados usados ​​pelas regras de segurança do Cloud Storage e como os erros ocorrem.

Regra

Uma rule é uma expressão avaliada para determinar se uma request tem permissão para executar uma ação desejada.

Tipos

Permitir

As regras allow consistem em um método, como read ou write , bem como em uma condição opcional. Quando uma regra é executada, a condição é avaliada e, se a condição for avaliada como true , o método desejado é permitido; caso contrário, o método será negado. Uma regra allow sem condição sempre permite o método desejado.

// Always allow method
allow <method>;

// Allow method if condition is true
allow <method>: if <condition>;

Atualmente, allow é o único tipo de regra compatível.

Métodos de solicitação

Ler

O método read cobre todas as solicitações em que dados ou metadados de arquivo são lidos, incluindo downloads de arquivos e leituras de metadados de arquivos.

// Always allow reads
allow read;

// Allow reads if condition evaluates to true
allow read: if <condition>;

Escrever

O método write cobre todas as solicitações em que dados ou metadados de arquivo são gravados, incluindo uploads de arquivos, exclusões de arquivos e atualizações de metadados de arquivos.

// Always allow writes
allow write;

// Allow writes if condition evaluates to true
allow write: if <condition>;

Corresponder

As regras são executadas quando uma request do usuário (como upload ou download de arquivo) corresponde a um caminho de arquivo coberto por uma regra. Uma match consiste em um caminho e um corpo, que deve conter pelo menos uma regra allow . Se nenhum caminho for correspondido, a solicitação será rejeitada.

Você pode match a um caminho totalmente nomeado ou inserir caracteres curinga para corresponder a todos os caminhos que se enquadram em um determinado padrão.

Segmentos de caminho

single_segment

Você pode usar segmentos de caminho único para criar uma regra que corresponda a um arquivo armazenado no Cloud Storage.

// Allow read at "path" if condition evaluates to true
match /path {
  allow read: if <condition>;
}

Vários segmentos de caminho e caminhos aninhados também são permitidos:

// Allow read at "path/to/object" if condition evaluates to true
match /path {
  match /to {
    match /object {
      allow read: if <condition>;
    }
  }
}

{single_segment_wildcard}

Se quiser aplicar uma regra a vários arquivos no mesmo caminho, você poderá usar um segmento de caminho curinga para corresponder todos os arquivos em um determinado caminho. Uma variável curinga é declarada em um caminho envolvendo uma variável entre chaves: {variable} . Esta variável pode ser acessada na instrução match como uma string .

// Allow read at any path "/*", if condition evaluates to true
match /{single_path} {
  // Matches "path", "to", or "object" but not "path/to/object"
  allow read: if <condition>;
}

Vários segmentos de caminho e caminhos aninhados também podem ter curingas:

// Allow read at any path "/path/*/newPath/*", if condition evaluates to true
match /path/{first_wildcard} {
  match /newPath/{second_wildcard} {
    // Matches "path/to/newPath/newObject" or "path/from/newPath/oldObject"
    allow read: if <condition>;
  }
}

{multi_segment_wildcard=**}

Se quiser corresponder qualquer número de segmentos de caminho em ou abaixo de um caminho, você poderá usar um curinga de vários segmentos, que corresponderá a todas as solicitações para e abaixo do local. Isso pode ser útil para fornecer ao usuário seu próprio espaço de armazenamento de formato livre ou criar regras que correspondam a muitos segmentos de caminho diferentes (como criar um conjunto de arquivos legíveis publicamente ou exigir autenticação para todas as gravações).

Um caminho curinga de vários segmentos é declarado de forma semelhante a um curinga de segmento único, com a adição de =** no final da variável: {variable=**} . Uma variável curinga multissegmento está disponível na instrução match como um objeto path .

// Allow read at any path "/**", if condition evaluates to true
match /{multi_path=**} {
  // Matches anything at or below this, from "path", "path/to", "path/to/object", ...
  allow read: if <condition>;
}

Solicitar

A variável request é fornecida em uma condição para representar a solicitação feita naquele caminho. A variável request possui diversas propriedades que podem ser usadas para decidir se a solicitação recebida será permitida.

Propriedades

auth

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

request.auth.token contém algumas ou todas as seguintes chaves:

Campo Descrição
email O endereço de e-mail associado à conta, se presente.
email_verified true se o usuário tiver verificado que tem acesso ao endereço email . Alguns provedores verificam automaticamente os endereços de e-mail de sua propriedade.
phone_number O número de telefone associado à conta, se presente.
name O nome de exibição do usuário, se definido.
sub O UID do Firebase do usuário. Isso é único dentro de um projeto.
firebase.identities Dicionário de todas as identidades associadas à conta deste usuário. As chaves do dicionário podem ser qualquer uma das seguintes: email , phone , google.com , facebook.com , github.com , twitter.com . Os valores do dicionário são matrizes de identificadores exclusivos para cada provedor de identidade associado à conta. Por exemplo, auth.token.firebase.identities["google.com"][0] contém o primeiro ID de usuário do Google associado à conta.
firebase.sign_in_provider O provedor de login usado para obter esse token. Pode ser uma das seguintes strings: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com .
firebase.tenant O tenantId associado à conta, se presente. por exemplo, tenant2-m6tyz

Se estiver usando autenticação personalizada, request.auth.token também contém quaisquer declarações personalizadas especificadas pelo desenvolvedor.

Quando um usuário não autenticado executa uma solicitação, request.auth é null .

// Allow requests from authenticated users
allow read, write: if request.auth != null;

path

A variável path contém o caminho no qual uma request está sendo executada.

// Allow a request if the first path segment equals "images"
allow read, write: if request.path[0] == 'images';

resource

A variável resource contém os metadados de um arquivo que está sendo carregado ou os metadados atualizados de um arquivo existente. Isto está relacionado à variável resource , que contém os metadados do arquivo atual no caminho solicitado, em oposição aos novos metadados.

// Allow a request if the new value is smaller than 5MB
allow read, write: if request.resource.size < 5 * 1024 * 1024;

request.resource contém as seguintes propriedades de resource :

Propriedade
name
bucket
metadata
size
contentType

time

A variável time contém um carimbo de data/hora que representa o horário atual do servidor em que uma solicitação está sendo avaliada. Você pode usar isso para fornecer acesso aos arquivos com base no tempo, como: permitir que os arquivos sejam carregados apenas até uma determinada data ou permitir que os arquivos sejam lidos apenas até uma hora após terem sido carregados.

// Allow a read if the file was created less than one hour ago
allow read: if request.time < resource.timeCreated + duration.value(1, 'h');

Muitas funções são fornecidas para escrever regras usando carimbos de data e hora e durações .

Recurso

A variável resource contém metadados de arquivos no Cloud Storage, como nome do arquivo, tamanho, horário de criação e metadados personalizados.

Propriedades

name

Uma string contendo o nome completo do arquivo, incluindo o caminho para o arquivo.

// Allow reads if the resource name is "path/to/object"
allow read: if resource.name == 'path/to/object'

bucket

Uma string que contém o intervalo do Google Cloud Storage em que este arquivo está armazenado.

// Allow reads of all resources in your bucket
allow read: if resource.bucket == '<your-cloud-storage-bucket>'

generation

Um int que contém a geração do objeto do arquivo no Google Cloud Storage. Usado para versionamento de objetos.

// Allow reads if the resource matches a known object version
allow read: if resource.generation == <known-generation>

metageneration

Um int que contém a metageração de objetos do Google Cloud Storage do arquivo. Usado para versionamento de objetos.

// Allow reads if the resource matches a known object metadata version
allow read: if resource.metageneration == <known-generation>

size

Um int contendo o tamanho do arquivo em bytes.

// Allow reads if the resource is less than 10 MB
allow read: if resource.size < 10 * 1024 * 1024;

timeCreated

Um carimbo de data/hora que representa quando o arquivo foi criado.

// Allow reads if the resource was created less than an hour ago
allow read: if resource.timeCreated < request.time + duration.value(60, "m")

updated

Um carimbo de data/hora que representa quando o arquivo foi atualizado pela última vez.

// Allow reads if the resource was updated less than an hour ago
allow read: if resource.updated < request.time + duration.value(60, "m")

md5Hash

Uma string contendo o hash MD5 do arquivo.

// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.md5Hash == resource.md5Hash;

crc32c

Uma string contendo o hash crc32c do arquivo.

// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.crc32c == resource.crc32c;

etag

Uma string contendo o etag do arquivo.

// Allow writes if the etag matches a known object etag
allow write: if resource.etag == <known-generation>

contentDisposition

Uma string contendo a disposição do conteúdo do arquivo.

// Allow reads if the content disposition matches a certain value
allow read: if resource.contentDisposition == 'inlined';

contentEncoding

Uma string contendo a codificação do conteúdo do arquivo.

// Allow reads if the content is encoded with gzip
allow read: if resource.contentEncoding == 'gzip';

contentLanguage

Uma string contendo o idioma do conteúdo do arquivo.

// Allow reads if the content language is Japanese
allow read: if resource.contentLanguage == 'ja';

contentType

Uma string contendo o tipo de conteúdo do arquivo.

// Allow reads if the content type is PNG.
allow read: if resource.contentType == 'image/png';

metadata

Um Map<String, String> contendo campos adicionais de metadados fornecidos pelo desenvolvedor.

// Allow reads if a certain metadata field matches a desired value
allow read: if resource.metadata.customProperty == 'customValue';

firestore.get e firestore.exists

As funções firestore.get() e firestore.exists() permitem acessar documentos no Cloud Firestore para avaliar critérios de autorização complexos.

As funções firestore.get() e firestore.exists() esperam caminhos de documentos totalmente especificados. Ao usar variáveis ​​para construir caminhos para firestore.get() e firestore.exists() , você precisa escapar explicitamente das variáveis ​​usando a sintaxe $(variable) .

firestore.get

Obtenha o conteúdo de um documento do Cloud Firestore.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.uid)).data.memberships
    }
  }
}

firestore.existe

Verifique se existe um documento do Cloud Firestore.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.uid))
    }
  }
}

Serviço

O service é a primeira declaração em um arquivo de regras de segurança do Cloud Storage e especifica a qual serviço essas regras serão aplicadas.

Nome

name

O nome das regras de serviço será aplicado. O único valor atual é firebase.storage .

// Specify the service name
service firebase.storage {
  match /b/{bucket}/o {
    ...
  }
}

Tipos de dados

A linguagem de regras permite verificar o tipo usando o operador is .

// For example
a is null
a is string

null

O tipo de dados null representa um valor não existente.

allow read: if request.auth != null;

bool

O tipo bool representa um valor booleano true ou false .

allow read: if true;   // always succeeds
allow write: if false; // always fails

Comparação

Os valores booleanos podem ser comparados usando os operadores == != .

Operações Booleanas

Operação Expressão
AND x && y
OR x || y
NOT !x

As operações entram em curto-circuito e podem retornar true , false ou um Error .

allow read: if true || false;   // always succeeds, short circuits at true
allow write: if false && true; // always fails, short circuits at false

int e float

Os tipos int e float representam números. Ints são: 0 , 1 , -2 , etc. , enquanto floats são: 1.0 , -2.0 , 3.33 , etc.

Ints são valores assinados de 64 bits e floats são valores compatíveis com IEEE 754 de 64 bits. Valores do tipo int serão forçados a float quando usados ​​em comparações e operações aritméticas com um valor float .

Comparação

Ints e floats podem ser comparados e ordenados usando os operadores == , != , > , < , >= e <= .

Aritmética

Ints e floats podem ser adicionados, subtraídos, multiplicados, divididos, modulados e negados:

Operação Expressão
Adição x + y
Subtração x - y
Multiplicação x * y
Divisão x / y
Módulo x % y
Negação -x

Funções matemáticas

As regras de segurança do Firebase para Cloud Storage também fornecem diversas funções auxiliares matemáticas para simplificar expressões:

Função Descrição
math.ceil(x) Teto do valor numérico
math.floor(x) Piso do valor numérico
math.round(x) Arredonde o valor de entrada para o int mais próximo
math.abs(x) Valor absoluto da entrada
math.isInfinite(x) Teste se o valor é ±∞ , retorna um bool
math.isNaN(x) Teste se o valor não é um número NaN , retorna um bool

string

Comparação

As strings podem ser comparadas e ordenadas lexograficamente usando os operadores == , != , > , < , >= e <= .

Concatenação

Strings podem ser concatenadas usando o operador + .

// Concatenate a file name and extension
'file' + '.txt'

Índice e intervalo

O operador index , string[] , retorna uma string que contém o caractere no índice fornecido na string.

// Allow reads of files that begin with 'a'
match /{fileName} {
  allow read: if fileName[0] == 'a';
}

O operador range , string[i:j] , retorna uma string que contém os caracteres entre os índices especificados, de i (inclusivo) até j (exclusivo). Se i ou j não forem especificados, o padrão será 0 e o tamanho da string, respectivamente, mas pelo menos i ou j deverão ser especificados para que o intervalo seja válido.

// Allow reads of files that begin with 'abcdef'
match /{fileName} {
  allow read: if fileName[0:6] == 'abcdef';
}

Os operadores index e range produzirão um erro se os índices fornecidos excederem os limites da string.

size

Retorna o número de caracteres da string.

// Allow files with names less than 10 characters
match /{fileName} {
  allow write: if fileName.size() < 10;
}

matches

Executa uma correspondência de expressão regular, retorna true se a string corresponder à expressão regular fornecida. Usa a sintaxe Google RE2 .

// Allow writes to files which end in ".txt"
match /{fileName} {
  allow write: if fileName.matches('.*\\.txt')
}

split

Divide uma string de acordo com uma expressão regular fornecida e retorna uma list de strings. Usa a sintaxe Google RE2 .

// Allow files named "file.*" to be uploaded
match /{fileName} {
  allow write: if fileName.split('.*\\..*')[0] == 'file'
}

path

Caminhos são nomes semelhantes a diretórios com correspondência de padrões opcional. A presença de uma barra / indica o início de um segmento de caminho.

path

Converte um argumento string em um path .

// Allow reads on a specific file path
match /{allFiles=**} {
  allow read: if allFiles == path('/path/to/file');
}

timestamp

Os carimbos de data e hora estão em UTC, com valores possíveis começando em 0001-01-01T00.00.00Z e terminando em 9999-12-31T23.59.59Z.

Comparação

Os carimbos de data e hora podem ser comparados e ordenados usando os operadores == , != , > , < , >= e <= .

Aritmética

Os carimbos de data/hora suportam adição e subtração entre carimbos de data/hora e durações da seguinte forma:

Expressão Resultado
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

date

Um valor timestamp contendo apenas year , month e day .

// Allow reads on the same day that the resource was created.
allow read: if request.time.date() == resource.timeCreated.date()

year

O valor do ano como um int, de 1 a 9999.

// Allow reads on all requests made before 2017
allow read: if request.time.year() < 2017

month

O valor do mês como um int, de 1 a 12.

// Allow reads on all requests made during the month of January
allow read: if request.time.month() == 1;

day

O dia atual do mês como um int, de 1 a 31.

// Allow reads on all requests made during the first day of each month
allow read: if request.time.day() == 1;

time

Um valor duration que contém a hora atual.

// Allow reads on all requests made before 12PM
allow read: if request.time.time() < duration.time(12, 0, 0, 0);

hours

O valor das horas como um int, de 0 a 23.

// Allow reads on all requests made before 12PM
allow read: if request.time.hours() < 12;

minutes

O valor dos minutos como um int, de 0 a 59.

// Allow reads during even minutes of every hour
allow read: if request.time.minutes() % 2 == 0;

seconds

O valor dos segundos como um int, de 0 a 59.

// Allow reads during the second half of each minute
allow read: if request.time.seconds() > 29;

nanos

Os segundos fracionários em nanos como um int.

// Allow reads during the first 0.1 seconds of each second
allow read: if request.time.nanos() < 100000000;

dayOfWeek

O dia da semana, de 1 (segunda-feira) a 7 (domingo).

// Allow reads on weekdays (Monday to Friday)
allow read: if request.time.dayOfWeek() < 6;

dayOfYear

O dia do ano atual, de 1 a 366.

// Allow reads every fourth day
allow read: if request.time.dayOfYear() % 4 == 0;

toMillis

Retorna o número atual de milissegundos desde a época Unix.

// Allow reads if the request is made before a specified time
allow read: if request.time.toMillis() < <milliseconds>;

duration

Os valores de duração são representados como segundos mais segundos fracionários em nanossegundos.

Comparação

As durações podem ser comparadas e ordenadas usando os operadores == , != , > , < , >= e <= .

Aritmética

As durações suportam adição e subtração entre carimbos de data/hora e durações da seguinte forma:

Expressão Resultado
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

seconds

O número de segundos na duração atual. Deve estar entre -315.576.000.000 e +315.576.000.000 inclusive.

nanos

O número de segundos fracionários (em nanossegundos) da duração atual. Deve estar entre -999.999.999 e +999.999.999 inclusive. Para segundos diferentes de zero e nanossegundos diferentes de zero, os sinais de ambos devem estar de acordo.

duration.value

As durações podem ser criadas usando a função duration.value(int magnitude, string units) , que cria uma duração de tempo a partir de uma determinada magnitude e unidade.

// All of these durations represent one hour:
duration.value(1, "h")
duration.value(60, "m")
duration.value(3600, "s")

unit possíveis são:

Duração unit
Semanas w
Dias d
Horas h
Minutos m
Segundos s
Milissegundos ms
Nanossegundos ns

duration.time

As durações podem ser criadas usando a função duration.time(int hours, int minutes, int seconds, int nanoseconds) , que cria uma duração de horas, minutos, segundos e nanossegundos fornecidos.

// Create a four hour, three minute, two second, one nanosecond duration
duration.time(4, 3, 2, 1)

list

Uma lista contém uma matriz ordenada de valores, que podem ser do tipo: null , bool , int , float , string , path , list , map , timestamp ou duration .

Dados x e y do tipo list e i e j do tipo int

Criação

Para criar uma lista, adicione valores entre colchetes:

// Create a list of strings
['apples', 'grapes', 'bananas', 'cheese', 'goats']

Comparação

As listas podem ser comparadas usando os operadores == != . A igualdade de duas listas exige que todos os valores sejam iguais.

Índice e intervalo

O operador index list[] , retorna o item no índice fornecido na lista.

// Allow reads of all files that begin with 'a'
match /{fileName} {
  allow read: if fileName[0] == 'a';
}

O operador range list[i:j] , retorna todos os itens em uma lista entre os índices especificados, de i (inclusivo) até j (exclusivo). Se i ou j não forem especificados, o padrão é 0 e o tamanho da lista, respectivamente, mas pelo menos i ou j devem ser especificados para que o intervalo seja válido.

// Allow reads of all files that begin with 'abcdef'
match /{fileName} {
  allow read: if fileName[0:6] == 'abcdef';
}

in

Retorna true se o valor desejado estiver presente na lista ou false se não estiver presente.

// Allow read if a filename has the string 'txt' in it
match /{fileName} {
  allow read: if 'txt' in fileName.split('\\.');
}

join

Combina uma lista de strings em uma única string, separada pela string fornecida.

// Allow reads if the joined array is 'file.txt'
allow read: if ['file', 'txt'].join('.') == 'file.txt';

size

O número de itens na lista.

// Allow read if there are three items in our list
allow read: if ['foo', 'bar', 'baz'].size() == 3;

hasAll

Retorna true se todos os valores estiverem presentes na lista.

// Allow read if one list has all items in the other list
allow read: if ['file', 'txt'].hasAll(['file', 'txt']);

map

Um mapa contém pares chave/valor, onde as chaves são strings e os valores podem ser: null , bool , int , float , string , path , list , map , timestamp ou duration .

Criação

Para criar um mapa, adicione pares chave/valor entre chaves:

// Create a map of strings to strings
{
  'mercury': 'mars',
  'rain': 'cloud',
  'cats': 'dogs',
}

Comparação

Os mapas podem ser comparados usando os operadores == != . A igualdade de dois mapas requer que todas as chaves estejam presentes em ambos os mapas e que todos os valores sejam iguais.

Índice

Os valores em um mapa são acessados ​​usando notação de colchetes ou pontos:

// Access custom metadata properties
allow read: if resource.metadata.property == 'property'
allow write: if resource.metadata['otherProperty'] == 'otherProperty'

Se uma chave não estiver presente, um error será retornado.

in

Retorna true se a chave desejada estiver presente no mapa ou false se não estiver presente.

// Allow reads if a property is present in the custom metadata
allow read: if property in resource.metadata;

size

O número de chaves no mapa.

// Allow reads if there's exactly one custom metadata key
allow read: if resource.metadata.size() == 1;

keys

Uma lista de todas as chaves no mapa.

// Allow reads if the first metadata key is 'myKey'
allow read: if resource.metadata.keys()[0] == 'myKey';

values

Uma lista de todos os valores no mapa, em ordem de chave.

// Allow reads if the first metadata value is 'myValue'
allow read: if resource.metadata.values()[0] == 'myValue';

Erros

Avaliação de erros

As regras de segurança do Firebase para Cloud Storage continuam a avaliação quando erros são encontrados. Isso é útil porque condicional && e || expressões podem absorver um erro se a condicional entrar em curto-circuito para false ou true respectivamente. Por exemplo:

Expressão Resultado
error && true error
error && false false
error || true true
error || false error

Locais comuns onde erros são gerados são: divisão por zero, acesso a valores em uma lista ou mapa que não existem e passagem de valores do tipo incorreto para uma função.

// Error if resource.size is zero
allow read: if 1000000 / resource.size;

// Error, key doesn't exist
allow read: if resource.metadata.nonExistentKey == 'value';

// Error, no unit 'y' exists
allow read: if request.time < resource.timeCreated + duration.value(1, 'y');