Se usó la API de Cloud Translation para traducir esta página.
Switch to English

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.

Las reglas de seguridad de la base de datos en tiempo real son configuraciones declarativas 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 utilizan 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.

Las reglas básicas identifican un conjunto de nodos a proteger, 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 simples declaraciones true y false , 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, 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 muestra 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 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 en declaraciones de reglas posteriores. Esta técnica le permite escribir condiciones de Reglas más complejas, algo que cubriremos 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 ruta constantes. En este ejemplo, estamos usando la variable $other para declarar una regla .validate que asegura que el widget no tiene hijos más que title y color . Cualquier escritura que pudiera resultar en la creación de niños 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 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 hijo baz con valor true . La regla ".read": false debajo de /foo/bar/ no tiene efecto aquí, ya que el acceso no puede ser revocado por una ruta secundaria.

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 a la seguridad basada en el usuario más adelante en esta guía.

Tenga en cuenta que .validate reglas .validate no funcionan 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 que al buscar la ruta /records/ se 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/ , esto arrojará un error PERMISSION_DENIED . Si evaluamos esta regla en el simulador de seguridad en nuestra consola de 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, el método de acceso se deniega 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 del nodo message1 porque la segunda regla siempre es false , aunque la primera regla siempre sea true .

Próximos pasos

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

  • Aprenda el siguiente concepto principal del lenguaje de reglas, las 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 .