Las reglas de seguridad de Firebase Realtime Database le permiten controlar el acceso a los datos almacenados en su base de datos. La sintaxis de reglas flexibles le permite crear reglas que coincidan con cualquier cosa, desde todas las escrituras en su base de datos hasta operaciones en nodos individuales.
Las reglas de seguridad de la base de datos en tiempo real son una configuración declarativa para su base de datos. Esto significa que las reglas se definen por separado de la lógica del producto. Esto tiene una serie de ventajas: los clientes no son responsables de hacer cumplir la seguridad, las implementaciones con errores no comprometerán sus datos y, quizás lo más importante, no hay necesidad de un árbitro intermedio, como un servidor, para proteger los datos del mundo.
Este tema describe la sintaxis y la estructura básicas de las reglas de seguridad de la base de datos en tiempo real que se utilizan para crear conjuntos de reglas completos.
Estructuración de sus reglas de seguridad
Las reglas de seguridad de la base de datos en tiempo real se componen de expresiones similares a JavaScript contenidas en un documento JSON. La estructura de sus reglas debe seguir la estructura de los datos que ha almacenado en su base de datos.
Las reglas básicas identifican un conjunto de nodos que deben protegerse, los métodos de acceso (por ejemplo, lectura, escritura) involucrados y las condiciones bajo las cuales se permite o deniega el acceso. En los siguientes ejemplos, nuestras condiciones serán declaraciones true
y false
simples, pero en el siguiente tema cubriremos formas más dinámicas de expresar condiciones.
Entonces, por ejemplo, si estamos tratando de asegurar un child_node
bajo un parent_node
, la sintaxis general a seguir es:
{ "rules": { "parent_node": { "child_node": { ".read": <condition>, ".write": <condition>, ".validate": <condition>, } } } }
Apliquemos este patrón. Por ejemplo, supongamos que está realizando un seguimiento de una lista de mensajes y tiene datos que se ven así:
{ "messages": { "message0": { "content": "Hello", "timestamp": 1405704370369 }, "message1": { "content": "Goodbye", "timestamp": 1405704395231 }, ... } }
Sus reglas deben estar estructuradas de manera similar. Aquí hay un conjunto de reglas para la seguridad de solo lectura que podría tener sentido para esta estructura de datos. Este ejemplo ilustra cómo especificamos los nodos de la base de datos a los que se aplican las reglas y las condiciones para evaluar las reglas en esos nodos.
{ "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" } } } }
Operaciones de reglas básicas
Hay tres tipos de reglas para hacer cumplir la seguridad según el tipo de operación que se realiza en los datos: .write
, .read
y .validate
. He aquí un breve resumen de sus propósitos:
Tipos de reglas | |
---|---|
.leer | Describe si los usuarios pueden leer los datos y cuándo. |
.escribe | Describe si se permite escribir datos y cuándo. |
.validar | Define el aspecto que tendrá un valor con el formato correcto, si tiene atributos secundarios y el tipo de datos. |
Variables de captura comodín
Todas las declaraciones de reglas apuntan a nodos. Una instrucción puede apuntar a un nodo específico o usar variables de captura comodín $
para apuntar a conjuntos de nodos en un nivel de la jerarquía. Utilice estas variables de captura para almacenar el valor de las claves de nodo para su uso dentro de declaraciones de reglas posteriores. Esta técnica le permite escribir condiciones de Reglas más complejas, algo que trataremos con más detalle en el siguiente tema.
{ "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')" } } } } }
Las variables dinámicas $
también se pueden usar en paralelo con nombres de rutas constantes. En este ejemplo, usamos la variable $other
para declarar una regla .validate
que garantiza que el widget
no tenga elementos secundarios además de title
y color
. Cualquier escritura que resulte en la creación de elementos secundarios adicionales fallará.
{ "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 } } } }
Cascada de reglas de lectura y escritura
Las reglas .read
y .write
funcionan de arriba hacia abajo, con reglas superficiales que anulan las reglas más profundas. Si una regla otorga permisos de lectura o escritura en una ruta en particular, también otorga acceso a todos los nodos secundarios debajo de ella. Considere la siguiente estructura:
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { /* ignored, since read was allowed already */ ".read": false } } } }
Esta estructura de seguridad permite leer /bar/
siempre que /foo/
contenga un baz
secundario con valor true
. La regla ".read": false
en /foo/bar/
no tiene efecto aquí, ya que una ruta secundaria no puede revocar el acceso.
Si bien puede no parecer inmediatamente intuitivo, esta es una parte poderosa del lenguaje de reglas y permite implementar privilegios de acceso muy complejos con un esfuerzo mínimo. Esto se ilustrará cuando nos adentremos en la seguridad basada en el usuario más adelante en esta guía.
Tenga en cuenta que las reglas .validate
no se aplican en cascada. Todas las reglas de validación deben cumplirse en todos los niveles de la jerarquía para que se permita una escritura.
Las reglas no son filtros
Las reglas se aplican de manera atómica. Eso significa que una operación de lectura o escritura falla inmediatamente si no hay una regla en esa ubicación o en una ubicación principal que otorga acceso. Incluso si se puede acceder a todas las rutas secundarias afectadas, la lectura en la ubicación principal fallará por completo. Considere esta estructura:
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
Sin comprender que las reglas se evalúan atómicamente, podría parecer que obtener la ruta /records/
devolvería rec1
pero no rec2
. El resultado real, sin embargo, es un error:
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 });
C objetivo
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
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
Dado que la operación de lectura en /records/
es atómica, y no hay una regla de lectura que otorgue acceso a todos los datos en /records/
, generará un error PERMISSION_DENIED
. Si evaluamos esta regla en el simulador de seguridad en nuestra consola Firebase , podemos ver que la operación de lectura fue denegada porque ninguna regla de lectura permitió el acceso a la ruta /records/
. Sin embargo, tenga en cuenta que la regla para rec1
nunca se evaluó porque no estaba en la ruta que solicitamos. Para obtener rec1
, necesitaríamos acceder a él directamente:
JavaScript
var db = firebase.database(); db.ref("records/rec1").once("value", function(snap) { // SUCCESS! }, function(err) { // error callback is not called });
C objetivo
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Rápido
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!
Declaraciones superpuestas
Es posible que se aplique más de una regla a un nodo. En el caso de que varias expresiones de reglas identifiquen un nodo, se deniega el método de acceso si alguna de las condiciones es 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" } } } }
En el ejemplo anterior, se denegarán las lecturas en el nodo message1
porque la segunda regla siempre es false
, aunque la primera regla siempre es true
.
Próximos pasos
Puede profundizar su comprensión de las reglas de seguridad de la base de datos en tiempo real de Firebase:
Aprenda el próximo concepto principal del lenguaje de reglas, condiciones dinámicas, que le permiten a sus reglas verificar la autorización del usuario, comparar datos existentes y entrantes, validar datos entrantes, verificar la estructura de consultas provenientes del cliente y más.
Revise los casos de uso de seguridad típicos y las definiciones de las reglas de seguridad de Firebase que los abordan .