获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Como funcionam as regras de segurança

A segurança pode ser uma das peças mais complexas do quebra-cabeça do desenvolvimento de aplicativos. Na maioria dos aplicativos, os desenvolvedores devem construir e executar um servidor que lide com autenticação (quem é um usuário) e autorização (o que um usuário pode fazer).

As regras de segurança do Firebase removem a camada intermediária (servidor) e permitem que você especifique permissões baseadas em caminho para clientes que se conectam diretamente aos seus dados. Use este guia para saber mais sobre como as regras são aplicadas às solicitações recebidas.

Selecione um produto para saber mais sobre suas regras.

Cloud Firestore

Estrutura básica

As regras de segurança do Firebase no Cloud Firestore e no Cloud Storage usam a seguinte estrutura e sintaxe:

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

Os seguintes conceitos-chave são importantes para entender à medida que você cria as regras:

  • Request: O método ou métodos invocados na instrução de allow . Estes são os métodos que você está permitindo a execução. Os métodos padrão são: get , list , create , update e delete . Os métodos de conveniência de read e write permitem amplo acesso de leitura e gravação no banco de dados ou caminho de armazenamento especificado.
  • Caminho: O banco de dados ou local de armazenamento, representado como um caminho de URI.
  • Regra: A instrução allow , que inclui uma condição que permite uma solicitação se ela for avaliada como verdadeira.

Regras de segurança versão 2

A partir de maio de 2019, a versão 2 das regras de segurança do Firebase já está disponível. A versão 2 das regras altera o comportamento dos curingas recursivos {name=**} . Você deve usar a versão 2 se planeja usar consultas de grupo de coleção . Você deve aceitar a versão 2 fazendo rules_version = '2'; a primeira linha em suas regras de segurança:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

Caminhos correspondentes

Todas as declarações de correspondência devem apontar para documentos, não para coleções. Uma declaração de correspondência pode apontar para um documento específico, como em match /cities/SF ou usar curingas para apontar para qualquer documento no caminho especificado, como em match /cities/{city} .

No exemplo acima, a instrução match usa a sintaxe curinga {city} . Isso significa que a regra se aplica a qualquer documento da coleção de cities , como /cities/SF ou /cities/NYC . Quando as expressões de allow na instrução de correspondência são avaliadas, a variável city resolverá o nome do documento da cidade, como SF ou NYC .

Subcoleções correspondentes

Os dados no Cloud Firestore são organizados em coleções de documentos, e cada documento pode estender a hierarquia por meio de subcoleções. É importante entender como as regras de segurança interagem com os dados hierárquicos.

Considere a situação em que cada documento da coleção de cities contém uma subcoleção de landmarks de referência. As regras de segurança se aplicam apenas no caminho combinado, portanto, os controles de acesso definidos na coleção de cities não se aplicam à subcoleção de landmarks de referência. Em vez disso, escreva regras explícitas para controlar o acesso às subcoleções:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read, write: if <condition>;

      // Explicitly define rules for the 'landmarks' subcollection
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}

Ao aninhar instruções de match , o caminho da instrução de match interna é sempre relativo ao caminho da instrução de match externa. Os seguintes conjuntos de regras são, portanto, equivalentes:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}
service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city}/landmarks/{landmark} {
      allow read, write: if <condition>;
    }
  }
}

Curingas recursivas

Se você deseja que as regras se apliquem a uma hierarquia arbitrariamente profunda, use a sintaxe curinga recursiva, {name=**} :

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

Ao usar a sintaxe curinga recursiva, a variável curinga conterá todo o segmento de caminho correspondente, mesmo se o documento estiver localizado em uma subcoleção profundamente aninhada. Por exemplo, as regras listadas acima corresponderiam a um documento localizado em /cities/SF/landmarks/coit_tower , e o valor da variável do document seria SF/landmarks/coit_tower .

Observe, no entanto, que o comportamento dos curingas recursivos depende da versão das regras.

Versão 1

As regras de segurança usam a versão 1 por padrão. Na versão 1, curingas recursivos correspondem a um ou mais itens de caminho. Eles não correspondem a um caminho vazio, então match /cities/{city}/{document=**} corresponde a documentos em subcoleções, mas não na coleção cities , enquanto match /cities/{document=**} corresponde a ambos os documentos no coleção e subcoleções de cities .

Curingas recursivos devem vir no final de uma instrução de correspondência.

Versão 2

Na versão 2 das regras de segurança, curingas recursivos correspondem a zero ou mais itens de caminho. match/cities/{city}/{document=**} corresponde a documentos em qualquer subcoleção, bem como documentos na coleção de cities .

Você deve aceitar a versão 2 adicionando rules_version = '2'; no topo de suas regras de segurança:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{city}/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

Você pode ter no máximo um curinga recursivo por instrução de correspondência, mas na versão 2, você pode colocar esse curinga em qualquer lugar na instrução de correspondência. Por exemplo:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the songs collection group
    match /{path=**}/songs/{song} {
      allow read, write: if <condition>;
    }
  }
}

Se você usar consultas de grupo de coleção , deverá usar a versão 2, consulte protegendo consultas de grupo de coleção .

Declarações de correspondência sobrepostas

É possível que um documento corresponda a mais de uma declaração de match . No caso em que várias expressões de allow correspondem a uma solicitação, o acesso é permitido se qualquer uma das condições for true :

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the 'cities' collection.
    match /cities/{city} {
      allow read, write: if false;
    }

    // Matches any document in the 'cities' collection or subcollections.
    match /cities/{document=**} {
      allow read, write: if true;
    }
  }
}

No exemplo acima, todas as leituras e gravações na coleção cities serão permitidas porque a segunda regra é sempre true , mesmo que a primeira regra seja sempre false .

Limites da regra de segurança

Ao trabalhar com regras de segurança, observe os seguintes limites:

Limite Detalhes
Número máximo de chamadas exist( exists() , get() e getAfter() por solicitação
  • 10 para solicitações de documento único e solicitações de consulta.
  • 20 para leituras de vários documentos, transações e gravações em lote. O limite anterior de 10 também se aplica a cada operação.

    Por exemplo, imagine que você crie uma solicitação de gravação em lote com 3 operações de gravação e que suas regras de segurança usem 2 chamadas de acesso a documentos para validar cada gravação. Nesse caso, cada gravação usa 2 de suas 10 chamadas de acesso e a solicitação de gravação em lote usa 6 de suas 20 chamadas de acesso.

Exceder qualquer um dos limites resulta em um erro de permissão negada.

Algumas chamadas de acesso a documentos podem ser armazenadas em cache e as chamadas armazenadas em cache não contam para os limites.

Profundidade máxima da instrução de match aninhada 10
Comprimento máximo do caminho, em segmentos de caminho, permitido em um conjunto de instruções de match aninhadas 100
Número máximo de variáveis ​​de captura de caminho permitidas em um conjunto de instruções de match aninhadas 20
Profundidade máxima de chamada de função 20
Número máximo de argumentos de função 7
Número máximo de associações de variáveis let por função 10
Número máximo de chamadas de função recursivas ou cíclicas 0 (não permitido)
Número máximo de expressões avaliadas por solicitação 1.000
Tamanho máximo de um conjunto de regras Os conjuntos de regras devem obedecer a dois limites de tamanho:
  • um limite de 256 KB no tamanho da fonte de texto do conjunto de regras publicada no Firebase console ou na CLI usando o firebase deploy .
  • um limite de 250 KB no tamanho do conjunto de regras compilado que resulta quando o Firebase processa a fonte e a torna ativa no back-end.

Armazenamento na núvem

Estrutura básica

As regras de segurança do Firebase no Cloud Firestore e no Cloud Storage usam a seguinte estrutura e sintaxe:

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

Os seguintes conceitos-chave são importantes para entender à medida que você cria as regras:

  • Request: O método ou métodos invocados na instrução de allow . Estes são os métodos que você está permitindo a execução. Os métodos padrão são: get , list , create , update e delete . Os métodos de conveniência de read e write permitem amplo acesso de leitura e gravação no banco de dados ou caminho de armazenamento especificado.
  • Caminho: O banco de dados ou local de armazenamento, representado como um caminho de URI.
  • Regra: A instrução allow , que inclui uma condição que permite uma solicitação se ela for avaliada como verdadeira.

Caminhos correspondentes

As regras de segurança do Cloud Storage match aos caminhos de arquivo usados ​​para acessar arquivos no Cloud Storage. As regras podem match a caminhos exatos ou curingas, e as regras também podem ser aninhadas. Se nenhuma regra de correspondência permitir um método de solicitação ou a condição for avaliada como false , a solicitação será negada.

Correspondências exatas

// Exact match for "images/profilePhoto.png"
match /images/profilePhoto.png {
  allow write: if <condition>;
}

// Exact match for "images/croppedProfilePhoto.png"
match /images/croppedProfilePhoto.png {
  allow write: if <other_condition>;
}

Correspondências aninhadas

// Partial match for files that start with "images"
match /images {
  // Exact match for "images/profilePhoto.png"
  match /profilePhoto.png {
    allow write: if <condition>;
  }

  // Exact match for "images/croppedProfilePhoto.png"
  match /croppedProfilePhoto.png {
    allow write: if <other_condition>;
  }
}

Correspondências de curinga

As regras também podem ser usadas para match a um padrão usando curingas. Um curinga é uma variável nomeada que representa uma única string, como profilePhoto.png , ou vários segmentos de caminho, como images/profilePhoto.png .

Um curinga é criado adicionando chaves ao redor do nome do curinga, como {string} . Um curinga de vários segmentos pode ser declarado adicionando =** ao nome do curinga, como {path=**} :

// Partial match for files that start with "images"
match /images {
  // Exact match for "images/*"
  // e.g. images/profilePhoto.png is matched
  match /{imageId} {
    // This rule only matches a single path segment (*)
    // imageId is a string that contains the specific segment matched
    allow read: if <condition>;
  }

  // Exact match for "images/**"
  // e.g. images/users/user:12345/profilePhoto.png is matched
  // images/profilePhoto.png is also matched!
  match /{allImages=**} {
    // This rule matches one or more path segments (**)
    // allImages is a path that contains all segments matched
    allow read: if <other_condition>;
  }
}

Se várias regras corresponderem a um arquivo, o resultado será o OR do resultado de todas as avaliações de regras. Ou seja, se alguma regra ao qual o arquivo corresponder for avaliada como true , o resultado será true .

Nas regras acima, o arquivo "images/profilePhoto.png" pode ser lido se uma das condition ou other_condition verdadeira, enquanto o arquivo "images/users/user:12345/profilePhoto.png" está sujeito apenas ao resultado de other_condition .

Uma variável curinga pode ser referenciada de dentro da match fornecer nome de arquivo ou autorização de caminho:

// Another way to restrict the name of a file
match /images/{imageId} {
  allow read: if imageId == "profilePhoto.png";
}

As regras de segurança do Cloud Storage não são em cascata, e as regras são avaliadas apenas quando o caminho da solicitação corresponde a um caminho com as regras especificadas.

Solicitar avaliação

Uploads, downloads, alterações de metadados e exclusões são avaliados usando a request enviada ao Cloud Storage. A variável de request contém o caminho do arquivo em que a solicitação está sendo executada, a hora em que a solicitação é recebida e o novo valor do resource se a solicitação for uma gravação. Cabeçalhos HTTP e estado de autenticação também estão incluídos.

O objeto de request também contém o ID exclusivo do usuário e a carga útil do Firebase Authentication no objeto request.auth , que será explicado mais detalhadamente na seção Autenticação dos documentos.

Uma lista completa de propriedades no objeto de request está disponível abaixo:

Propriedade Modelo Descrição
auth mapa<string, string> Quando um usuário está conectado, fornece uid , o ID exclusivo do usuário e token , um mapa de declarações JWT do Firebase Authentication. Caso contrário, será null .
params mapa<string, string> Mapa contendo os parâmetros de consulta da solicitação.
path caminho Um path representa o caminho em que a solicitação está sendo executada.
resource mapa<string, string> O novo valor do recurso, presente apenas em solicitações de write .
time carimbo de data/hora Um carimbo de data/hora que representa a hora do servidor em que a solicitação é avaliada.

Avaliação de recursos

Ao avaliar as regras, você também pode avaliar os metadados do arquivo que está sendo carregado, baixado, modificado ou excluído. Isso permite que você crie regras complexas e poderosas que fazem coisas como permitir que apenas arquivos com determinados tipos de conteúdo sejam carregados ou apenas arquivos maiores que um determinado tamanho sejam excluídos.

As regras de segurança do Firebase para Cloud Storage fornecem metadados de arquivo no objeto de resource , que contém pares de chave/valor dos metadados exibidos em um objeto do Cloud Storage. Essas propriedades podem ser inspecionadas em solicitações de read ou write para garantir a integridade dos dados.

Em solicitações de write (como uploads, atualizações de metadados e exclusões), além do objeto de resource , que contém metadados de arquivo para o arquivo que existe atualmente no caminho da solicitação, você também pode usar o objeto request.resource , que contém um subconjunto dos metadados do arquivo a serem gravados se a gravação for permitida. Você pode usar esses dois valores para garantir a integridade dos dados ou impor restrições de aplicativos, como tipo ou tamanho de arquivo.

Uma lista completa de propriedades no objeto de resource está disponível abaixo:

Propriedade Modelo Descrição
name corda O nome completo do objeto
bucket corda O nome do bucket em que esse objeto reside.
generation int A geração do objeto do Google Cloud Storage deste objeto.
metageneration int A metageração do objeto do Google Cloud Storage deste objeto.
size int O tamanho do objeto em bytes.
timeCreated carimbo de data/hora Um carimbo de data/hora que representa a hora em que um objeto foi criado.
updated carimbo de data/hora Um carimbo de data/hora que representa a hora em que um objeto foi atualizado pela última vez.
md5Hash corda Um hash MD5 do objeto.
crc32c corda Um hash crc32c do objeto.
etag corda A etag associada a este objeto.
contentDisposition corda A disposição de conteúdo associada a este objeto.
contentEncoding corda A codificação de conteúdo associada a este objeto.
contentLanguage corda O idioma do conteúdo associado a este objeto.
contentType corda O tipo de conteúdo associado a este objeto.
metadata mapa<string, string> Pares de chave/valor de metadados personalizados adicionais especificados pelo desenvolvedor.

request.resource contém tudo isso com exceção de generation , metageneration , etag , timeCreated e updated .

Limites das regras de segurança

Ao trabalhar com regras de segurança, observe os seguintes limites:

Limite Detalhes
Número máximo de chamadas firestore.exists() e firestore.get() por solicitação

2 para solicitações de documento único e solicitações de consulta.

Exceder esse limite resulta em um erro de permissão negada.

As chamadas de acesso aos mesmos documentos podem ser armazenadas em cache e as chamadas armazenadas em cache não contam para os limites.

Exemplo completo

Juntando tudo, você pode criar um exemplo completo de regras para uma solução de armazenamento de imagens:

service firebase.storage {
 match /b/{bucket}/o {
   match /images {
     // Cascade read to any image type at any path
     match /{allImages=**} {
       allow read;
     }

     // Allow write files to the path "images/*", subject to the constraints:
     // 1) File is less than 5MB
     // 2) Content type is an image
     // 3) Uploaded content type matches existing content type
     // 4) File name (stored in imageId wildcard variable) is less than 32 characters
     match /{imageId} {
       allow write: if request.resource.size < 5 * 1024 * 1024
                    && request.resource.contentType.matches('image/.*')
                    && request.resource.contentType == resource.contentType
                    && imageId.size() < 32
     }
   }
 }
}

Banco de dados em tempo real

Estrutura básica

No Realtime Database, as regras de segurança do Firebase consistem em expressões semelhantes a JavaScript contidas em um documento JSON.

Eles usam a seguinte sintaxe:

{
  "rules": {
    "<<path>>": {
    // Allow the request if the condition for each method is true.
      ".read": <<condition>>,
      ".write": <<condition>>,
      ".validate": <<condition>>
    }
  }
}

Existem três elementos básicos na regra:

  • Caminho: O local do banco de dados. Isso espelha a estrutura JSON do seu banco de dados.
  • Request: Esses são os métodos que a regra usa para conceder acesso. As regras de read e write concedem amplo acesso de leitura e gravação, enquanto as regras de validate atuam como uma verificação secundária para conceder acesso com base em dados recebidos ou existentes.
  • Condição: A condição que permite uma solicitação se for avaliada como verdadeira.

Como as regras se aplicam aos caminhos

No Realtime Database, as regras se aplicam atomicamente, o que significa que as regras em nós pai de nível superior substituem as regras em nós filho mais granulares e as regras em um nó mais profundo não podem conceder acesso a um caminho pai. Você não pode refinar ou revogar o acesso em um caminho mais profundo em sua estrutura de banco de dados se já o concedeu para um dos caminhos pai.

Considere as seguintes regras:

{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          // ignored, since read was allowed already
          ".read": false
        }
     }
  }
}

Essa estrutura de segurança permite que /bar/ seja lido sempre que /foo/ contiver um filho baz com valor true . A ".read": false em /foo/bar/ não tem efeito aqui, pois o acesso não pode ser revogado por um caminho filho.

Embora possa não parecer imediatamente intuitivo, essa é uma parte poderosa da linguagem de regras e permite que privilégios de acesso muito complexos sejam implementados com o mínimo de esforço. Isso é particularmente útil para segurança baseada no usuário .

No entanto, as regras .validate não entram em cascata. Todas as regras de validação devem ser satisfeitas em todos os níveis da hierarquia para que uma gravação seja permitida.

Além disso, como as regras não se aplicam a um caminho pai, a operação de leitura ou gravação falhará se não houver uma regra no local solicitado ou em um local pai que conceda acesso. Mesmo que cada caminho filho afetado seja acessível, a leitura no local pai falhará completamente. Considere esta estrutura:

{
  "rules": {
    "records": {
      "rec1": {
        ".read": true
      },
      "rec2": {
        ".read": false
      }
    }
  }
}

Sem entender que as regras são avaliadas atomicamente, pode parecer que buscar o caminho /records/ retornaria rec1 mas não rec2 . O resultado real, no entanto, é um erro:

JavaScript
var db = firebase.database();
db.ref("records").once("value", function(snap) {
  // success method is not called
}, function(err) {
  // error callback triggered with PERMISSION_DENIED
});
Objetivo-C
Observação: este produto Firebase não está disponível no destino App Clip.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
  // success block is not called
} withCancelBlock:^(NSError * _Nonnull error) {
  // cancel block triggered with PERMISSION_DENIED
}];
Rápido
Observação: este produto Firebase não está disponível no destino App Clip.
var ref = FIRDatabase.database().reference()
ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // success block is not called
}, withCancelBlock: { error in
    // cancel block triggered with PERMISSION_DENIED
})
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // success method is not called
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback triggered with PERMISSION_DENIED
  });
});
DESCANSO
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Como a operação de leitura em /records/ é atômica e não há regra de leitura que conceda acesso a todos os dados em /records/ , isso gerará um erro PERMISSION_DENIED . Se avaliarmos essa regra no simulador de segurança em nosso console do Firebase , veremos que a operação de leitura foi negada:

Attempt to read /records with auth=Success(null)
    /
    /records

No .read rule allowed the operation.
Read was denied.

A operação foi negada porque nenhuma regra de leitura permitiu o acesso ao caminho /records/ , mas observe que a regra para rec1 nunca foi avaliada porque não estava no caminho que solicitamos. Para buscar rec1 , precisaríamos acessá-lo diretamente:

JavaScript
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
Objetivo-C
Observação: este produto Firebase não está disponível no destino App Clip.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Rápido
Observação: este produto Firebase não está disponível no destino App Clip.
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records/rec1");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // SUCCESS!
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback is not called
  }
});
DESCANSO
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Variável de local

As regras do Realtime Database oferecem suporte a uma variável $location para corresponder a segmentos de caminho. Use o prefixo $ na frente de seu segmento de caminho para corresponder sua regra a qualquer nó filho ao longo do caminho.

  {
    "rules": {
      "rooms": {
        // This rule applies to any child of /rooms/, the key for each room id
        // is stored inside $room_id variable for reference
        "$room_id": {
          "topic": {
            // The room's topic can be changed if the room id has "public" in it
            ".write": "$room_id.contains('public')"
          }
        }
      }
    }
  }

Você também pode usar a $variable em paralelo com nomes de caminho constantes.

  {
    "rules": {
      "widget": {
        // a widget can have a title or color attribute
        "title": { ".validate": true },
        "color": { ".validate": true },

        // but no other child paths are allowed
        // in this case, $other means any key excluding "title" and "color"
        "$other": { ".validate": false }
      }
    }
  }