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

Aprenda a sintaxe principal da linguagem Realtime Database Security Rules

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

As regras de segurança do Firebase Realtime Database permitem controlar o acesso aos dados armazenados em seu banco de dados. A sintaxe de regras flexíveis permite que você crie regras que correspondam a qualquer coisa, desde todas as gravações em seu banco de dados até operações em nós individuais.

As regras de segurança do Realtime Database são uma configuração declarativa para seu banco de dados. Isso significa que as regras são definidas separadamente da lógica do produto. Isso tem várias vantagens: os clientes não são responsáveis ​​por reforçar a segurança, implementações de bugs não comprometerão seus dados e, talvez o mais importante, não há necessidade de um árbitro intermediário, como um servidor, para proteger os dados do mundo.

Este tópico descreve a sintaxe básica e a estrutura das Regras de Segurança do Realtime Database usadas para criar conjuntos de regras completos.

Estruturando suas regras de segurança

As regras de segurança do Realtime Database são compostas de expressões semelhantes a JavaScript contidas em um documento JSON. A estrutura de suas regras deve seguir a estrutura dos dados armazenados em seu banco de dados.

As regras básicas identificam um conjunto de nós a serem protegidos, os métodos de acesso (por exemplo, leitura, gravação) envolvidos e as condições sob as quais o acesso é permitido ou negado. Nos exemplos a seguir, nossas condições serão simples declarações true e false , mas no próximo tópico abordaremos formas mais dinâmicas de expressar condições.

Assim, por exemplo, se estivermos tentando proteger um child_node sob um parent_node , a sintaxe geral a seguir é:

{
  "rules": {
    "parent_node": {
      "child_node": {
        ".read": <condition>,
        ".write": <condition>,
        ".validate": <condition>,
      }
    }
  }
}

Vamos aplicar este padrão. Por exemplo, digamos que você esteja acompanhando uma lista de mensagens e tenha dados assim:

{
  "messages": {
    "message0": {
      "content": "Hello",
      "timestamp": 1405704370369
    },
    "message1": {
      "content": "Goodbye",
      "timestamp": 1405704395231
    },
    ...
  }
}

Suas regras devem ser estruturadas de maneira semelhante. Aqui está um conjunto de regras para segurança somente leitura que podem fazer sentido para essa estrutura de dados. Este exemplo ilustra como especificamos os nós do banco de dados aos quais as regras se aplicam e as condições para avaliar as regras nesses nós.

{
  "rules": {
    // For requests to access the 'messages' node...
    "messages": {
      // ...and the individual wildcarded 'message' nodes beneath
      // (we'll cover wildcarding variables more a bit later)....
      "$message": {

        // For each message, allow a read operation if <condition>. In this
        // case, we specify our condition as "true", so read access is always granted.
        ".read": "true",

        // For read-only behavior, we specify that for write operations, our
        // condition is false.
        ".write": "false"
      }
    }
  }
}

Operações de regras básicas

Existem três tipos de regras para impor a segurança com base no tipo de operação que está sendo executada nos dados: .write , .read e .validate . Aqui está um breve resumo de seus propósitos:

Tipos de regras
.ler Descreve se e quando os dados podem ser lidos pelos usuários.
.Escreva Descreve se e quando os dados podem ser gravados.
.validar Define a aparência de um valor formatado corretamente, se ele possui atributos filho e o tipo de dados.

Variáveis ​​de captura curinga

Todas as instruções de regras apontam para nós. Uma instrução pode apontar para um nó específico ou usar variáveis ​​de captura $ curinga para apontar para conjuntos de nós em um nível da hierarquia. Use essas variáveis ​​de captura para armazenar o valor das chaves do nó para uso nas instruções de regras subsequentes. Essa técnica permite que você escreva condições de Regras mais complexas , algo que abordaremos com mais detalhes no próximo tópico.

{
  "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')"
        }
      }
    }
  }
}

As variáveis $ dinâmicas também podem ser usadas em paralelo com nomes de caminhos constantes. Neste exemplo, estamos usando a variável $other para declarar uma regra .validate que garante que o widget não tenha filhos além de title e color . Qualquer gravação que resultasse na criação de filhos adicionais falharia.

{
  "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 }
    }
  }
}

Cascata de regras de leitura e gravação

As regras .read e .write funcionam de cima para baixo, com regras mais superficiais substituindo regras mais profundas. Se uma regra conceder permissões de leitura ou gravação em um caminho específico, ela também concederá acesso a todos os nós filho sob ela. Considere a seguinte estrutura:

{
  "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 será ilustrado quando entrarmos na segurança baseada no usuário mais adiante neste guia.

Observe que as regras .validate não são colocadas 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.

Regras não são filtros

As regras são aplicadas de maneira atômica. Isso significa que uma operação de leitura ou gravação falhará imediatamente se não houver uma regra nesse local 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 porque nenhuma regra de leitura permitiu o acesso ao caminho /records/ . No entanto, 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!

Declarações sobrepostas

É possível que mais de uma regra seja aplicada a um nó. No caso em que várias expressões de regras identificam um nó, o método de acesso é negado se alguma das condições for false :

{
  "rules": {
    "messages": {
      // A rule expression that applies to all nodes in the 'messages' node
      "$message": {
        ".read": "true",
        ".write": "true"
      },
      // A second rule expression applying specifically to the 'message1` node
      "message1": {
        ".read": "false",
        ".write": "false"
      }
    }
  }
}

No exemplo acima, as leituras no nó message1 serão negadas porque a segunda regra é sempre false , mesmo que a primeira regra seja sempre true .

Próximos passos

Você pode aprofundar sua compreensão das regras de segurança do Firebase Realtime Database:

  • Aprenda o próximo grande conceito da linguagem de regras, condições dinâmicas, que permitem que suas regras verifiquem a autorização do usuário, comparem dados existentes e recebidos, validem dados recebidos, verifiquem a estrutura de consultas provenientes do cliente e muito mais.

  • Revise os casos de uso de segurança típicos e as definições das regras de segurança do Firebase que os abordam .