Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Aprenda la sintaxis principal del lenguaje de reglas de Realtime Database

Las reglas de seguridad de Firebase Realtime Database te permiten controlar el acceso a los datos almacenados en tu base de datos. La sintaxis de reglas flexible le permite crear reglas que coincidan con cualquier cosa, desde todas las escrituras en su base de datos hasta operaciones en nodos individuales.

Base de datos en tiempo real en Reglas de seguridad son de 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 básica y la estructura de las reglas de seguridad de Realtime Database que se usan para crear conjuntos de reglas completos.

Estructurar 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.

Reglas básicas identificar un conjunto de nodos a ser asegurado, los métodos de acceso (por ejemplo, leer, escribir) involucrados, y condiciones en que ya sea se permite o deniega el acceso. En los siguientes ejemplos, las condiciones serán simples true y false declaraciones, pero en el siguiente tema que vamos a cubrir más formas dinámicas para expresar condiciones.

Así, 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, digamos 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
    },
    ...
  }
}

Tus reglas deben estar estructuradas de manera similar. A continuación, se incluye un conjunto de reglas de seguridad de solo lectura que podrían 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 reforzar la seguridad en función del tipo de operación que se realizan sobre los datos: .write , .read y .validate . Aquí hay un resumen rápido de sus propósitos:

Tipos de reglas
.leer Describe si los usuarios pueden leer los datos y cuándo.
.escribir Describe si se permite la escritura de datos y cuándo.
.validar Define cómo se verá un valor con el formato correcto, si tiene atributos secundarios y el tipo de datos.

Variables de captura de comodines

Todas las declaraciones de reglas apuntan a nodos. Una declaración puede apuntar a un nodo específico o utilizar $ variables de captura de comodín a punto de 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 las declaraciones de reglas posteriores. Esta técnica le permite escribir las 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')"
        }
      }
    }
  }
}

La dinámica $ variables que también pueden utilizarse en paralelo con los nombres de ruta constantes. En este ejemplo, estamos usando la $other variable para declarar una .validate regla que asegura que widget no tiene hijos que no sean title y color . Cualquier escritura que pudiera dar como resultado la creación de elementos secundarios adicionales fallaría.

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

.read y .write reglas funcionan desde arriba hacia abajo, con reglas más superficiales primordiales reglas más profundas. Si un prestamista concede la regla de lectura o permiso de escritura en un camino particular, entonces también permite el acceso a todos los nodos hijos en virtud del mismo. 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 permite que la seguridad /bar/ para ser leído desde el momento en /foo/ contiene un niño baz con el valor true . El ".read": false regla bajo /foo/bar/ no tiene efecto aquí, ya que el acceso no puede ser revocada por un camino niño.

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 mínimo esfuerzo. Esto se ilustrará cuando entremos en la seguridad basada en el usuario más adelante en esta guía.

Tenga en cuenta que .validate reglas no caen 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 otorgue 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 entender que las reglas se evalúan de forma atómica, podría parecer como ir a buscar el /records/ ruta volvería rec1 pero no rec2 . Sin embargo, el resultado real 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
  });
});
DESCANSAR
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ómico, y no hay ninguna regla de lectura que permite el acceso a todos los datos bajo /records/ , esto va a lanzar una PERMISSION_DENIED error. Si evaluamos esta regla en el simulador de la seguridad en nuestra consola Firebase , podemos ver que la operación de lectura se negó porque hay una regla de lectura permite el acceso al /records/ ruta. Sin embargo, nota que la regla para rec1 nunca fue evaluada debido a que no estaba en el camino que habíamos solicitado. A buscar rec1 , tendríamos que 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
  }
});
DESCANSAR
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 reglas expresiones identifican un nodo, el método de acceso es denegado si cualquiera 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 lee en el message1 nodo será negado porque las segundas reglas siempre es false , a pesar de que la primera regla es siempre true .

Próximos pasos

Puede profundizar su comprensión de las reglas de seguridad de Firebase Realtime Database:

  • Aprender el siguiente concepto importante de la lengua en reglas dinámicas condiciones , que permita que su autorización de usuario consultar las reglas, comparar los datos existentes y entrantes, validar los datos entrantes, comprobar la estructura de las consultas procedentes del cliente, y mucho más.

  • Revisar los casos de uso típicos de seguridad y las definiciones Firebase reglas de seguridad que abordarlos .