Catch up on everthing we announced at this year's Firebase Summit. Learn more

Condiciones de uso en Reglas de base de datos en tiempo real

Esta guía se basa en el aprender el lenguaje básico de seguridad Reglas Firebase guía para mostrar cómo añadir condiciones a su base de datos en tiempo real Firebase normas de seguridad.

El bloque de construcción fundamental de la base de datos en tiempo real Reglas de Seguridad es la condición. Una condición es una expresión booleana que determina si se debe permitir o denegar una operación en particular. Para las reglas básicas, el uso de true y false literales como las condiciones prefectamenta funciona bien. Pero el lenguaje de reglas de seguridad de Realtime Database le brinda formas de escribir condiciones más complejas que pueden:

  • Verificar la autenticación del usuario
  • Evaluar los datos existentes frente a los datos enviados recientemente
  • Acceda y compare diferentes partes de su base de datos
  • Validar datos entrantes
  • Utilice la estructura de las consultas entrantes para la lógica de seguridad

Uso de $ Variables para capturar segmentos de ruta

Puede capturar partes de la trayectoria de una lectura o escritura de declaración de variables de captura con el $ prefijo. Esto sirve como comodín y almacena el valor de esa clave para su uso dentro de las condiciones de las reglas:

{
  "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 resulte en la creación de niños 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 }
    }
  }
}

Autenticación

Uno de los patrones de reglas de seguridad más comunes es controlar el acceso según el estado de autenticación del usuario. Por ejemplo, es posible que su aplicación desee permitir que solo los usuarios que hayan iniciado sesión escriban datos.

Si su aplicación utiliza la autenticación Firebase, la request.auth variable contiene la información de autenticación del cliente que solicita los datos. Para obtener más información acerca de request.auth , consulte la documentación de referencia .

Firebase Authentication se integra con Firebase Realtime Database para permitirte controlar el acceso a los datos por usuario usando condiciones. Una vez que un usuario se autentica, la auth variable en su base de datos en tiempo real normas Seguridad Reglas se rellenará con la información del usuario. Esta información incluye su identificador único ( uid ), así como datos de cuentas vinculadas, tales como una identificación de Facebook o una dirección de correo electrónico y otra información. Si implementa un proveedor de autenticación personalizado, puede agregar sus propios campos a la carga útil de autenticación de su usuario.

En esta sección, se explica cómo combinar el lenguaje de las reglas de seguridad de Firebase Realtime Database con información de autenticación sobre tus usuarios. Al combinar estos dos conceptos, puede controlar el acceso a los datos según la identidad del usuario.

La auth Variable

El predefinido auth variable en las reglas es nula antes de la autenticación se lleva a cabo.

Una vez que un usuario se autentica con autenticación Firebase que contendrá los siguientes atributos:

proveedor El método de autenticación utilizado ("contraseña", "anónimo", "facebook", "github", "google" o "twitter").
uid Una identificación de usuario única, garantizada para ser única en todos los proveedores.
simbólico El contenido del token de ID de autenticación de Firebase. Consulte la documentación de referencia para auth.token para más detalles.

Aquí es una regla de ejemplo que utiliza la auth variable para garantizar que cada usuario sólo puede escribir en una ruta específica de usuario:

{
  "rules": {
    "users": {
      "$user_id": {
        // grants write access to the owner of this user account
        // whose uid must exactly match the key ($user_id)
        ".write": "$user_id === auth.uid"
      }
    }
  }
}

Estructuración de su base de datos para admitir condiciones de autenticación

Por lo general, es útil estructurar su base de datos de una manera que facilite la escritura de reglas. Un patrón común para almacenar datos de usuario en la base de datos en tiempo real es almacenar todos sus usuarios en un solo users nodo cuyos hijos son los uid valores para cada usuario. Si quisiera restringir el acceso a estos datos de modo que solo el usuario que inició sesión pueda ver sus propios datos, sus reglas se verían así.

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid"
      }
    }
  }
}

Trabajar con notificaciones personalizadas de autenticación

Para aplicaciones que requieren control de acceso personalizado para los diferentes usuarios, autenticación Firebase permite a los desarrolladores a reclamaciones pondrán al usuario Firebase . Estas afirmaciones son accesibles en el auth.token variable en sus reglas. Aquí está un ejemplo de reglas que hacen uso de la hasEmergencyTowel notificación personalizada:

{
  "rules": {
    "frood": {
      // A towel is about the most massively useful thing an interstellar
      // hitchhiker can have
      ".read": "auth.token.hasEmergencyTowel === true"
    }
  }
}

Los desarrolladores que crean sus propios tokens de autenticación personalizados pueden añadir opcionalmente reclamaciones a estas fichas. Estas afirmaciones son avaialble en la auth.token variable en sus reglas.

Datos existentes frente a datos nuevos

El predefinidos data variable se utiliza para referirse a los datos antes de una operación de escritura tiene lugar. Por el contrario, la newData variable contiene los nuevos datos que existen si la operación de escritura se realiza correctamente. newData representa el resultado combinado de los nuevos datos que se escriben y los datos existentes.

Para ilustrar, esta regla nos permitiría crear nuevos registros o eliminar los existentes, pero no realizar cambios en los datos no nulos existentes:

// we can write as long as old data or new data does not exist
// in other words, if this is a delete or a create, but not an update
".write": "!data.exists() || !newData.exists()"

Referenciar datos en otras rutas

Cualquier dato se puede utilizar como criterio para las reglas. Utilizando la predefinido variables de root , data , y newData , podemos acceder a cualquier camino, ya que existiría antes o después de un evento de escritura.

Considere este ejemplo, lo que permite que las operaciones de escritura, siempre y cuando el valor de la /allow_writes/ nodo es true , el nodo padre no tiene un readOnly pabellón conjunto, y hay un niño llamado foo en los datos recién escritos:

".write": "root.child('allow_writes').val() === true &&
          !data.parent().child('readOnly').exists() &&
          newData.child('foo').exists()"

Validando datos

La aplicación de estructuras de datos y validar el formato y contenido de los datos debe hacerse utilizando .validate reglas, que se mantiene en marcha sólo después de un .write regla tiene éxito para conceder acceso. A continuación una muestra .validate definición de la regla que sólo permite las fechas en el formato AAAA-MM-DD entre los años 1900-2099, que se comprueba mediante una expresión regular.

".validate": "newData.isString() &&
              newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"

Los .validate reglas son el único tipo de regla de seguridad que no hacer en cascada. Si alguna regla de validación falla en cualquier registro secundario, se rechazará toda la operación de escritura. Además, las definiciones Validar se ignoran cuando se eliminan los datos (es decir, cuando el nuevo valor se escribe es null ).

Estos pueden parecer puntos triviales, pero de hecho son características importantes para escribir poderosas reglas de seguridad de Firebase Realtime Database. Considere las siguientes reglas:

{
  "rules": {
    // write is allowed for all paths
    ".write": true,
    "widget": {
      // a valid widget must have attributes "color" and "size"
      // allows deleting widgets (since .validate is not applied to delete rules)
      ".validate": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99
        ".validate": "newData.isNumber() &&
                      newData.val() >= 0 &&
                      newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical
        // /valid_colors/ index
        ".validate": "root.child('valid_colors/' + newData.val()).exists()"
      }
    }
  }
}

Con esta variante en mente, observe los resultados de las siguientes operaciones de escritura:

JavaScript
var ref = db.ref("/widget");

// PERMISSION_DENIED: does not have children color and size
ref.set('foo');

// PERMISSION DENIED: does not have child color
ref.set({size: 22});

// PERMISSION_DENIED: size is not a number
ref.set({ size: 'foo', color: 'red' });

// SUCCESS (assuming 'blue' appears in our colors list)
ref.set({ size: 21, color: 'blue'});

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child('size').set(99);
C objetivo
Nota: Este producto no está disponible Firebase en el objetivo de Clip aplicación.
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"];

// PERMISSION_DENIED: does not have children color and size
[ref setValue: @"foo"];

// PERMISSION DENIED: does not have child color
[ref setValue: @{ @"size": @"foo" }];

// PERMISSION_DENIED: size is not a number
[ref setValue: @{ @"size": @"foo", @"color": @"red" }];

// SUCCESS (assuming 'blue' appears in our colors list)
[ref setValue: @{ @"size": @21, @"color": @"blue" }];

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
[[ref child:@"size"] setValue: @99];
Rápido
Nota: Este producto no está disponible Firebase en el objetivo de Clip aplicación.
var ref = FIRDatabase.database().reference().child("widget")

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo")

// PERMISSION DENIED: does not have child color
ref.setValue(["size": "foo"])

// PERMISSION_DENIED: size is not a number
ref.setValue(["size": "foo", "color": "red"])

// SUCCESS (assuming 'blue' appears in our colors list)
ref.setValue(["size": 21, "color": "blue"])

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("widget");

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo");

// PERMISSION DENIED: does not have child color
ref.child("size").setValue(22);

// PERMISSION_DENIED: size is not a number
Map<String,Object> map = new HashMap<String, Object>();
map.put("size","foo");
map.put("color","red");
ref.setValue(map);

// SUCCESS (assuming 'blue' appears in our colors list)
map = new HashMap<String, Object>();
map.put("size", 21);
map.put("color","blue");
ref.setValue(map);

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
DESCANSAR
# PERMISSION_DENIED: does not have children color and size
curl -X PUT -d 'foo' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION DENIED: does not have child color
curl -X PUT -d '{"size": 22}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION_DENIED: size is not a number
curl -X PUT -d '{"size": "foo", "color": "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# SUCCESS (assuming 'blue' appears in our colors list)
curl -X PUT -d '{"size": 21, "color": "blue"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# If the record already exists and has a color, this will
# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
# will fail to validate
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Ahora vamos a ver la misma estructura, pero utilizando .write reglas en lugar de .validate :

{
  "rules": {
    // this variant will NOT allow deleting records (since .write would be disallowed)
    "widget": {
      // a widget must have 'color' and 'size' in order to be written to this path
      ".write": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99, ONLY IF WE WRITE DIRECTLY TO SIZE
        ".write": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical valid_colors/ index
        // BUT ONLY IF WE WRITE DIRECTLY TO COLOR
        ".write": "root.child('valid_colors/'+newData.val()).exists()"
      }
    }
  }
}

En esta variante, cualquiera de las siguientes operaciones tendría éxito:

JavaScript
var ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.set({size: 99999, color: 'red'});

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child('size').set(99);
C objetivo
Nota: Este producto no está disponible Firebase en el objetivo de Clip aplicación.
Firebase *ref = [[Firebase alloc] initWithUrl:URL];

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
[ref setValue: @{ @"size": @9999, @"color": @"red" }];

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
[[ref childByAppendingPath:@"size"] setValue: @99];
Rápido
Nota: Este producto no está disponible Firebase en el objetivo de Clip aplicación.
var ref = Firebase(url:URL)

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.setValue(["size": 9999, "color": "red"])

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.childByAppendingPath("size").setValue(99)
Java
Firebase ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
Map<String,Object> map = new HashMap<String, Object>();
map.put("size", 99999);
map.put("color", "red");
ref.setValue(map);

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child("size").setValue(99);
DESCANSAR
# ALLOWED? Even though size is invalid, widget has children color and size,
# so write is allowed and the .write rule under color is ignored
curl -X PUT -d '{size: 99999, color: "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# ALLOWED? Works even if widget does not exist, allowing us to create a widget
# which is invalid and does not have a valid color.
# (allowed by the write rule under "color")
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Esto ilustra las diferencias entre .write y .validate reglas. Como se ha demostrado, todas estas reglas deberán redactarse utilizando .validate , con la posible excepción de la newData.hasChildren() regla, que dependerá de si las supresiones se debe permitir.

Reglas basadas en consultas

A pesar de que no se puede utilizar reglas como filtros , se puede limitar el acceso a subconjuntos de datos mediante el uso de parámetros de consulta en las reglas. Uso query. expresiones en sus reglas para otorgar acceso de lectura o escritura en función de los parámetros de consulta.

Por ejemplo, la siguiente regla basada en la consulta utiliza reglas de seguridad basadas en el usuario y reglas basados en consultas para restringir el acceso a los datos en el baskets de recogida solamente a las cestas de la compra el usuario activa posee:

"baskets": {
  ".read": "auth.uid != null &&
            query.orderByChild == 'owner' &&
            query.equalTo == auth.uid" // restrict basket access to owner of basket
}

La siguiente consulta, que incluye los parámetros de consulta en la regla, tendría éxito:

db.ref("baskets").orderByChild("owner")
                 .equalTo(auth.currentUser.uid)
                 .on("value", cb)                 // Would succeed

Sin embargo, las consultas que no incluyen los parámetros de la regla de fallarían con un PermissionDenied de error:

db.ref("baskets").on("value", cb)                 // Would fail with PermissionDenied

También puede utilizar reglas basadas en consultas para limitar la cantidad de datos que descarga un cliente mediante operaciones de lectura.

Por ejemplo, la siguiente regla limita el acceso de lectura solo a los primeros 1000 resultados de una consulta, ordenados por prioridad:

messages: {
  ".read": "query.orderByKey &&
            query.limitToFirst <= 1000"
}

// Example queries:

db.ref("messages").on("value", cb)                // Would fail with PermissionDenied

db.ref("messages").limitToFirst(1000)
                  .on("value", cb)                // Would succeed (default order by key)

La siguiente query. Las expresiones están disponibles en Reglas de seguridad de base de datos en tiempo real.

Expresiones de reglas basadas en consultas
Expresión Escribe Descripción
query.orderByKey
query.orderByPriority
query.orderByValue
booleano Verdadero para consultas ordenadas por clave, prioridad o valor. Falso de lo contrario.
query.orderByChild cuerda
nulo
Utilice una cadena para representar la ruta relativa a un nodo secundario. Por ejemplo, query.orderByChild == "address/zip" . Si la consulta no está ordenada por un nodo secundario, este valor es nulo.
query.startAt
query.endAt
query.equalTo
cuerda
número
booleano
nulo
Recupera los límites de la consulta en ejecución o devuelve un valor nulo si no hay un conjunto de límites.
query.limitToFirst
query.limitToLast
número
nulo
Recupera el límite de la consulta en ejecución o devuelve un valor nulo si no hay ningún límite establecido.

Próximos pasos

Después de esta discusión sobre las condiciones, tiene una comprensión más sofisticada de las Reglas y está listo para:

Aprenda a manejar casos de uso básicos y conozca el flujo de trabajo para desarrollar, probar e implementar reglas:

Funciones de Learn Rules que son específicas de Realtime Database: