API de reglas de seguridad de Firebase Database

Regla: Tipos

.read

Otorga a un cliente acceso de lectura a una ubicación de Firebase Realtime Database.

Una regla .read es un tipo de regla de seguridad que otorga al cliente acceso de lectura a una ubicación de Firebase Realtime Database. Por ejemplo:

 ".read": "auth != null && auth.provider == 'twitter'"

El valor de una regla .read es una string, que se evalúa como un subconjunto de la sintaxis de expresión de JavaScript con algunos cambios de comportamiento para aumentar la claridad y la precisión. Una regla .read que otorga permiso para leer una ubicación también permitirá la lectura de cualquier elemento secundario de esa ubicación, incluso si estos tienen sus propias reglas .read que fallan.

Una regla .read tiene acceso a todas las variables de regla de Firebase Realtime Database, excepto newData.

.write

Otorga a un cliente acceso de escritura para una ubicación de Firebase Realtime Database.

Una regla .write es un tipo de regla de seguridad que otorga a un cliente acceso de escritura en una ubicación de Firebase Realtime Database. Por ejemplo:

".write": "auth != null && auth.token.isAdmin == true"

El valor de una regla .write es una string, que se evalúa como un subconjunto de la sintaxis de expresión de JavaScript con algunos cambios de comportamiento para aumentar la claridad y la precisión. Una regla .write que otorga permiso para escribir en una ubicación también permitirá escribir en cualquier elemento subordinado de esa ubicación, incluso si estos tienen sus propias reglas .write que fallan.

Una regla .write tiene acceso a todas las variables de regla de Firebase Realtime Database.

.validate

Se usa una vez que una regla .write otorga acceso para garantizar que los datos que se escriben se ajusten a un esquema específico.

Una regla .validate se usa una vez que una regla .write otorga acceso para garantizar que los datos que se escriben se ajustan a un estándar específico. Además de una .write que otorga acceso, todas las reglas .validate relevantes deben tener éxito antes de que se permita una operación de escritura. Por ejemplo:

".validate": "newData.hasChildren(['name', 'age'])"

El valor de una regla .validate es una string, que se evalúa como un subconjunto de la sintaxis de expresión de JavaScript con algunos cambios de comportamiento para aumentar la claridad y la precisión.

Una regla .validate tiene acceso a todas las variables de regla de Firebase Realtime Database.

.indexOn

Mejora el rendimiento de las consultas, ya que le indica a Firebase Realtime Database qué claves deseas que se indexen tus datos.

La regla .indexOn le indica a los servidores de Firebase Realtime Database que indexen claves específicas en tus datos para mejorar el rendimiento de las consultas. Por ejemplo, en una base de datos con una colección de datos de dinosaurios, podemos indicarle a Firebase Realtime Database que optimice las consultas antes de que se devuelvan desde los servidores agregando esta regla:

{
  "rules": {
    "dinosaurs": {
      ".indexOn": ["height", "length"]
    }
  }
}

Si quieres obtener más información sobre la regla .indexOn, consulta la sección de la guía de seguridad sobre cómo indexar tus datos.

Regla: Variables

auth

Una variable que contiene la carga útil del token si se autentica un cliente, o null si el cliente no está autenticado.

Firebase Realtime Database te permite autenticarte fácilmente en varios proveedores integrados y generará tokens de autenticación para ellos. Después de que un usuario se autentica con uno de los proveedores integrados, la variable auth contendrá lo siguiente:

Campo Descripción
provider El método de autenticación utilizado (p. ej., "password", "anonymous", "facebook", "github", "google" o "twitter").
uid Un ID de usuario único entre todos los proveedores.
token El contenido del token de ID de Firebase Auth. Consulta auth.token.

A modo de ejemplo, podríamos tener una regla como la siguiente para permitir que los usuarios creen comentarios siempre que almacenen su ID de usuario con el comentario:

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && newData.child('user_id').val() == auth.uid"
    }
  }
}

También podríamos crear una regla como la siguiente para permitir que los usuarios creen comentarios siempre que accedan con Facebook:

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && auth.provider == 'facebook'"
    }
  }
}

token de autenticación

Una variable que incluye el contenido del token de ID de Firebase Auth.

El token contiene algunas de las siguientes claves o todas ellas:

Campo Descripción
email Dirección de correo electrónico asociada con la cuenta, si está presente.
email_verified true si el usuario verificó que tiene acceso a la dirección email. Algunos proveedores verifican automáticamente las direcciones de correo electrónico de su propiedad.
phone_number Número de teléfono asociado con la cuenta, si está presente.
name Nombre visible del usuario, si se configuró.
sub UID de Firebase del usuario. Es único dentro de un proyecto.
firebase.identities Diccionario de todas las identidades asociadas con la cuenta del usuario. Las claves del diccionario pueden ser cualquiera de las siguientes: email, phone, google.com, facebook.com, github.com y twitter.com. Los valores del diccionario son arreglos de identificadores únicos para cada proveedor de identidad asociado con la cuenta. Por ejemplo, auth.token.firebase.identities["google.com"][0] contiene el primer ID de usuario de Google asociado a la cuenta.
firebase.sign_in_provider Proveedor de acceso utilizado para obtener este token. Puede ser una de las siguientes strings: custom, password, phone, anonymous, google.com, facebook.com, github.com y twitter.com.
firebase.tenant El tenantId asociado con la cuenta, si está presente, p. ej., tenant2-m6tyz

Si usas la autenticación personalizada, auth.token también contiene las credenciales reclamaciones especificadas por el desarrollador.

Todos estos valores se pueden usar en las reglas. Por ejemplo, para restringir el acceso a las Cuentas de Google asociadas a una dirección de gmail.com, podríamos agregar la siguiente regla:

{
  "rules": {
    ".read": "auth != null",
    "gmailUsers": {
      "$uid": {
        ".write": "auth.token.email_verified == true && auth.token.email.matches(/.*@gmail.com$/)"
      }
    }
  }
}

Para completar la información, también se incluyen los siguientes campos en auth.token, pero es poco probable que sean útiles para las reglas.

Campo Descripción
iss La entidad emisora del token.
aud El público del token.
auth_time La última vez que el usuario se autenticó con una credencial utilizando el dispositivo que recibió el token.
iat La hora a la que se emitió el token.
exp La hora a la que vence el token.

$location

Una variable que se puede usar para hacer referencia a la clave de un $location que se usó con anterioridad en una estructura de reglas.

Cuando tienes una $location en la estructura de reglas, puedes usar una variable $ coincidente dentro de la expresión de la regla para obtener el nombre del elemento secundario real que se está leyendo o escribiendo. Entonces, supongamos que queremos otorgar a cada usuario acceso de lectura y escritura a su propia ubicación de /users/<user>. Podríamos usar lo siguiente:

{
  "rules": {
    "users": {
      "$user": {
        ".read": "auth.uid === $user",
        ".write": "auth.uid === $user"
      }
    }
  }
}

Cuando un cliente intenta acceder a /users/barney, la ubicación predeterminada de $user coincidirá, y $user será igual a "barney". Por lo tanto, la regla .read verificará si es auth.uid === 'barney'. Como resultado, la lectura de /users/barney solo tendrá éxito si el cliente se autentica con un uid de "barney".

ahora

Contiene la cantidad de milisegundos desde el tiempo Unix según los servidores de Firebase Realtime Database.

La variable now contiene la cantidad de milisegundos desde la época UNIX según los servidores de Firebase Realtime Database. Por ejemplo, puedes usar esto para validar que la hora created de un usuario nunca esté configurada en una hora futura:

{
  "rules": {
    "users": {
      "$user": {
        "created": {
          ".validate": "newData.val() < now"
        }
      }
    }
  }
}

raíz

Una RuleDataSnapshot que corresponde a los datos actuales en la raíz de Firebase Realtime Database.

La variable raíz te proporciona una RuleDataSnapshot que corresponde a los datos actuales en la raíz de Firebase Realtime Database. Puedes usarlo para leer los datos de la base de datos en las expresiones de reglas. Por ejemplo, si quisiéramos permitir que los usuarios lean /comments solo si su /users/<id>/active se configuró como verdadera, podríamos usar lo siguiente:

{
  "rules": {
    "comments": {
      ".read": "root.child('users').child(auth.uid).child('active').val() == true"
    }
  }
}

Entonces, si /users/barney/active contenía el valor true, se autenticó un usuario con un uid de "barney" podría escribir en el nodo /comments.

datos

Una RuleDataSnapshot que corresponde a los datos actuales en Firebase Realtime Database en la ubicación de la regla que se está ejecutando.

La variable de datos te proporciona una RuleDataSnapshot que corresponde a los datos actuales en la ubicación de la base de datos de la regla que se está ejecutando (a diferencia de la raíz, que te brinda los datos de la raíz de la base de datos).

Por ejemplo, si deseas permitir que cualquier cliente acceda a /users/<user> si /users/<user>/public se establece como verdadero, puedes usar lo siguiente:

{
  "rules": {
    "users": {
      "$user": {
        ".read": "data.child('public').val() == true"
      }
    }
  }
}

La variable de datos está disponible en .read, .write y .validate reglas.

newData

Una RuleDataSnapshot que corresponde a los datos que se generarán si se permite la escritura.

Para las reglas .write y .validate, la variable newData proporciona una RuleDataSnapshot que corresponde a los datos que se obtendrán si se permite la escritura (es una “combinación” de los datos existentes más los datos nuevos que se escriben). Por lo tanto, si quieres asegurarte de que cada usuario tenga nombre y edad, podrías usar lo siguiente:

{
  "rules": {
    "users": {
      "$user": {
        ".read": true,
        ".write": true,
        ".validate": "newData.hasChildren(['name', 'age'])"
      }
    }
  }
}

Dado que newData fusiona los datos existentes con los nuevos, se comporta correctamente incluso en el caso de actualizaciones. Por ejemplo:

var fredRef = firebase.database().ref("users/fred");
// Valid since we have a name and age.
fredRef.set({ name: "Fred", age: 19 });
// Valid since we are updating the name but there's already an age.
fredRef.child("age").set(27);
// Invalid since the .validate rule will no longer be true.
fredRef.child("name").remove();

La variable newData no está disponible en las reglas .read, ya que no se escriben datos nuevos. Solo debes usar data.

RuleDataSnapshot: métodos

val()

Obtiene el valor primitivo (string, number, boolean o null) de esta RuleDataSnapshot.

Valor que se muestra: (String, Number, Boolean, Null): Es el valor básico de esta RuleDataSnapshot.

A diferencia de DataSnapshot.val(), si llamas a val() en una RuleDataSnapshot que tiene datos secundarios, no se mostrará un objeto que contenga los elementos secundarios. En su lugar, mostrará un valor centinela especial. Esto garantiza que las reglas siempre funcionen de manera muy eficiente.

Como consecuencia, siempre debes usar child() para acceder a elementos secundarios (p.ej., data.child('name').val(), no data.val().name).

En este ejemplo, solo se permite la lectura si el elemento secundario isReadable se establece como verdadero en la ubicación que se lee.

".read": "data.child('isReadable').val() == true"

hijo()

Obtiene una RuleDataSnapshot para la ubicación en la ruta de acceso relativa especificada.

Argumentos: childPath.String Es una ruta de acceso relativa a la ubicación de los datos secundarios.

Valor que se muestra: RuleDataSnapshot: Es la RuleDataSnapshot de la ubicación secundaria.

La ruta de acceso relativa puede ser un nombre secundario simple (p.ej., “fred”) o una ruta de acceso separada por barras más profundas (p.ej., “fred/name/first”). Si la ubicación secundaria no tiene datos, se muestra una RuleDataSnapshot vacía.

En este ejemplo, solo se permite la lectura si el elemento secundario isReadable se establece como verdadero en la ubicación que se lee.

".read": "data.child('isReadable').val() == true"

superior()

Obtiene una RuleDataSnapshot para la ubicación superior.

Valor que se muestra: RuleDataSnapshot: Es la RuleDataSnapshot de la ubicación superior.

Si esta instancia hace referencia a la raíz de Firebase Realtime Database, no tiene elementos superiores y parent() fallará, lo que hará que se omita la expresión de la regla actual (como un error).

Este ejemplo solo permite la lectura si el elemento del mismo nivel isReadable se establece como verdadero.

".read": "data.parent().child('isReadable').val() == true"

hasChild(childPath)

Muestra el valor true si el elemento secundario especificado existe.

Argumentos: childPath.String Es una ruta de acceso relativa a la ubicación de un posible elemento secundario.

Valor que se muestra: Booleano: true si existen datos en la ruta de acceso secundaria especificada. más false.

En este ejemplo, solo se permite que se escriban los datos si contienen un “nombre” secundario.

".validate": "newData.hasChild('name')"

tieneNiños([niños])

Comprueba la existencia de elementos secundarios.

Argumentos: children Array opcional: Es un array de claves secundarias que deben existir.

Valor que se muestra: Boolean - true si existen elementos secundarios (los especificados). más false.

Si no se proporcionan argumentos, el resultado será verdadero si la RuleDataSnapshot tiene algún elemento secundario. Si se proporciona un array de nombres secundarios, el resultado será verdadero solo si todos los elementos secundarios especificados existen en la RuleDataSnapshot.

Este ejemplo solo permite que se escriban los datos si contienen uno o más elementos secundarios.

".validate": "newData.hasChildren()"

Este ejemplo solo permite que se escriban los datos si contienen “name” y "edad" hijos o hijas.

".validate": "newData.hasChildren(['name', 'age'])"

existe()

Muestra true si esta RuleDataSnapshot contiene datos.

Valor que se muestra: Boolean - true si RuleDataSnapshot contiene datos. más false.

La función existe muestra verdadero si esta RuleDataSnapshot contiene datos. Es simplemente una función conveniente, ya que data.exists() es equivalente a data.val() != null.

Este ejemplo permite una operación de escritura en esta ubicación, siempre y cuando no haya datos existentes.

".write": "!data.exists()"

getPriority()

Obtiene la prioridad de los datos de una RuleDataSnapshot.

Valor que se muestra: (String, Number, Null): Es la prioridad de los datos de esta RuleDataSnapshot.

Este ejemplo garantiza que los datos nuevos que se escriben tienen prioridad

".validate": "newData.getPriority() != null"

Númerodenúmero()

Muestra true si esta RuleDataSnapshot contiene un valor numérico.

Valor que se muestra: Boolean - true si los datos son numéricos. más false.

Este ejemplo garantiza que los datos nuevos que se escriben tienen una “edad” secundaria con un valor numérico.

".validate": "newData.child('age').isNumber()"

isString()

Muestra true si esta RuleDataSnapshot contiene un valor de string.

Valor que se muestra: Boolean - true si los datos son String. más false.

En este ejemplo, se garantiza que los datos nuevos que se escriben tengan un “name” secundario. con un valor de cadena.

".validate": "newData.child('name').isString()

isBoolean()

Muestra true si esta RuleDataSnapshot contiene un valor booleano.

Valor que se muestra: Boolean - true si los datos son Boolean más false.

Este ejemplo garantiza que los datos nuevos que se escriben tienen elementos secundarios "activos" con un valor booleano.

".validate": "newData.child('active').isBoolean()"

String: Propiedades

duración

Muestra la longitud de la string.

Valor que se muestra: Number. Es la cantidad de caracteres en la string.

En este ejemplo, se requiere que la cadena tenga al menos 10 caracteres.

".validate": "newData.isString() && newData.val().length >= 10"

Cadena: Métodos

contiene(subcadena)

Muestra true si la string contiene la subcadena especificada.

Argumentos: substring.String Es una subcadena que se debe buscar.

Valor que se muestra: Boolean - true si la string contiene la subcadena especificada. más false.

En este ejemplo, se requiere que los datos sean una cadena que contenga "@".

".validate": "newData.isString() && newData.val().contains('@')"

comienza con(subcadena)

El resultado es verdadero si la string comienza con la subcadena especificada.

Argumentos: substring.String Es una subcadena que se debe buscar al principio.

Valor que se muestra: Boolean - true si la string contiene la subcadena especificada. más false.

En este ejemplo, se permite el acceso de lectura si auth.token.identifier comienza con "internal-"

".read": "auth.token.identifier.beginsWith('internal-')"

termina con(subcadena)

El resultado es verdadero si la string termina con la subcadena especificada.

Argumentos: substring.String Es una subcadena que se debe buscar al final.

Valor que se muestra: Boolean - true si la string termina con la subcadena especificada. más false.

Este ejemplo permite el acceso de lectura si auth.token.identifier termina con "@company.com"

".read": "auth.token.identifier.endsWith('@company.com')"

reemplazar(subcadena, reemplazo)

Muestra una copia de la string con todas las instancias de una subcadena especificada reemplazadas por la string de reemplazo especificada.

Argumentos: substring String. Es una subcadena que se debe buscar. replacement String: Es una cadena con la que se reemplaza la subcadena.

Valor que se muestra: String. Es la cadena nueva después de reemplazar la subcadena con el reemplazo.

El método replace() difiere levemente del método replace() de JavaScript en que reemplaza todas las instancias de una subcadena especificada por la cadena de reemplazo especificada, no solo la primera instancia.

Dado que los puntos no están permitidos en las claves, debemos escapar las cadenas con puntos antes de almacenarlos. Un ejemplo de esto sería con las direcciones de correo electrónico. Supongamos que tenemos una lista de direcciones de correo electrónico incluidas en la lista de entidades permitidas en nuestro nodo /whitelist/:

{
 "user": {
   "$uid": {
     "email": <email>
   }
 },
 "whitelist": {
   "fred@gmail%2Ecom": true,
   "barney@aol%2Ecom": true
 }
}

Podemos crear una regla que solo permita agregar usuarios si su correo electrónico está en el nodo /whitelist/:

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "true",
        ".write": "root.child('whitelist').child(newData.child('email').val().replace('.', '%2E')).exists()"
      }
    }
  }
}

toLowerCase()

Muestra una copia de la cadena convertida en minúsculas.

Valor que se muestra: String. Es la cadena convertida a minúsculas.

En este ejemplo, se permite el acceso de lectura si auth.token.identifier, como minúscula, existe en /users.

".read": "root.child('users').child(auth.token.identifier.toLowerCase()).exists()"

toUpperCase()

Muestra una copia de la cadena convertida en mayúsculas.

Valor que se muestra: String. Es la cadena convertida en mayúsculas.

En este ejemplo, se permite el acceso de lectura si auth.token.identifier (todas las mayúsculas y minúsculas) existe en /users.

".read": "root.child('users').child(auth.token.identifier.toUpperCase()).exists()"

coincidencias(regex)

Muestra verdadero si la string coincide con el literal de expresión regular especificado.

Valor que se muestra: Boolean - true si la string coincide con el literal de expresión regular, regex; más false.

Consulta la documentación de regex de reglas completa.

Operadores

+ (agregar)

Se usa para agregar variables o para concatenar cadenas.

En el siguiente ejemplo, se garantiza que el valor nuevo incremente el valor existente exactamente en uno. Esto es útil para implementar un contador:

".write": "newData.val() === data.val() + 1"
".validate": "root.child('room_names/' + $room_id).exists()"

- (negar o restar)

Se usa para negar un valor o restar dos valores en una expresión de reglas.

Esta regla de validación verifica que el valor nuevo sea lo contrario a un valor secundario en la ubicación:

".validate": "newData.val() === -(data.child('quantity').val())"

En el siguiente ejemplo, se usa la resta para garantizar que solo se puedan leer los mensajes de los últimos diez minutos:

".read": "newData.child('timestamp').val() > (now - 600000)"

* (multiplicar)

Se usa para multiplicar variables en una expresión de reglas.

Esta regla de validación verifica si el valor nuevo es igual al producto de precio y cantidad (dos valores existentes):

".validate": "newData.val() === data.child('price').val() * data.child('quantity').val()"

/ (dividir)

Se usa para dividir variables en una expresión de reglas.

En el siguiente ejemplo, la regla de validación se asegura de que los datos almacenados sean el promedio del total de datos almacenados en otro lugar:

".validate": "newData.val() === data.parent().child('sum').val() / data.parent().child('numItems').val()"

% (módulo)

Se usa para encontrar el resto de la división de una variable por otra en una expresión de reglas.

Esta regla valida que solo se puedan escribir números pares:

".validate": "newData.val() % 2 === 0"

=== (es igual a)

Se usa para verificar si dos variables en una expresión de reglas tienen el mismo tipo y valor.

La siguiente regla usa el operador === para otorgar acceso de escritura solo al propietario de la cuenta de usuario. El UID del usuario debe coincidir exactamente con la clave ($user_id) para que la regla se evalúe como verdadera.

"users": {
  ".write": "$user_id === auth.uid"
}

!== (no es igual a)

Se usa para verificar si dos variables en una expresión de reglas no son iguales.

La siguiente regla de lectura garantiza que solo los usuarios que accedieron puedan leer los datos:

".read": "auth !== null"

& (Y)

Se evalúa como verdadero si ambos operandos son verdaderos. Se usa para evaluar varias condiciones en una expresión de reglas.

La siguiente regla de validación verifica que los datos nuevos sean una cadena de menos de 100 caracteres:

".validate": "newData.isString() && newData.val().length < 100"

|| (O)

Se evalúa como verdadero si un operando de la expresión de reglas es verdadero.

En este ejemplo, podemos escribir siempre que no existan datos antiguos o nuevos. En otras palabras, podemos escribir si estamos borrando o creando datos, pero no actualizando datos.

".write": "!data.exists() || !newData.exists()"

! (NO)

Se evalúa como verdadero si su único operando es falso. En las expresiones de reglas, el símbolo ! suele usarse para ver si los datos se escribieron en una ubicación.

La siguiente regla solo permite el acceso de escritura si no hay datos en la ubicación especificada:

".write": "!data.exists()"

> (mayor que)

Se usa para verificar si un valor es mayor que otro valor en una expresión de reglas.

Esta regla de validación verifica que la cadena que se escribe no sea una cadena vacía:

".validate": "newData.isString() && newData.val().length > 0"

< (menor que)

Se usa para verificar si un valor es menor que otro valor en una expresión de reglas.

Esta regla de validación verifica que una cadena tenga menos de 20 caracteres:

".validate": "newData.isString() && newData.val().length < 20"

>= (mayor que o igual que)

Se usa para verificar si un valor es mayor o igual que otro valor en una expresión de reglas.

Esta regla de validación verifica que la cadena que se escribe no sea una cadena vacía:

".validate": "newData.isString() && newData.val().length >= 1"

<= (menor que o igual que)

Se usa para verificar si un valor es menor o igual que otro valor en una expresión de reglas.

Esta regla de validación garantiza que no se puedan agregar datos nuevos en el futuro:

".validate": "newData.val() <= now"

¿? (operador ternario)

Se usa para evaluar una expresión de reglas condicionales.

El operador ternario toma tres operandos. El operando antes del símbolo ? es la condición. Si la condición se evalúa como verdadera, se evalúa el segundo operando. Si la condición es falsa, se evalúa el tercer operando.

Para la siguiente regla de validación, el valor nuevo puede ser un número o un booleano. Si es un número, debe ser mayor que 0.

".validate": "newData.isNumber() ? newData.val() > 0 : newData.isBoolean()"