Lenguaje de las reglas de seguridad

Las reglas de seguridad de Firebase aprovechan los lenguajes flexibles, poderosos y personalizados que admiten un rango amplio de complejidad y nivel de detalle. Puedes hacer que las reglas para tu app sean tan específicas o generales como necesites. Las reglas de Realtime Database usan una sintaxis similar a la de JavaScript en una estructura JSON. Las reglas de Cloud Firestore y Cloud Storage usan un lenguaje basado en el Common Expression Language (CEL), que se basa en CEL con declaraciones match y allow que admiten el acceso condicional.

Sin embargo, hay una curva de aprendizaje debido a que estos son lenguajes personalizados. Usa esta guía para comprender mejor el lenguaje de las reglas a medida que profundizas en reglas más complejas.

Selecciona un producto para obtener más información sobre sus reglas.

Estructura básica

Cloud Firestore

Las reglas de seguridad de Firebase en Cloud Firestore y Cloud Storage utilizan la siguiente estructura y sintaxis:

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

Es importante comprender los siguientes conceptos clave cuando compiles las reglas:

  • Solicitud: El método o los métodos invocados en la declaración allow que permites que se ejecuten. Los métodos estándar son get, list, create, update y delete. Los métodos de conveniencia read y write permiten un amplio acceso de lectura y escritura en la base de datos o ruta de almacenamiento especificada.
  • Ruta de acceso: La ubicación de la base de datos o del almacenamiento, representada como una ruta URI.
  • Regla: La declaración allow, que incluye una condición que permite una solicitud si se evalúa como verdadera.

Cada uno de estos conceptos se describe con más detalle a continuación.

Cloud Storage

Las reglas de seguridad de Firebase en Cloud Firestore y Cloud Storage utilizan la siguiente estructura y sintaxis:

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

Es importante comprender los siguientes conceptos clave cuando compiles las reglas:

  • Solicitud: El método o los métodos invocados en la declaración allow que permites que se ejecuten. Los métodos estándar son get, list, create, update y delete. Los métodos de conveniencia read y write permiten un amplio acceso de lectura y escritura en la base de datos o ruta de almacenamiento especificada.
  • Ruta de acceso: La ubicación de la base de datos o del almacenamiento, representada como una ruta URI.
  • Regla: La declaración allow, que incluye una condición que permite una solicitud si se evalúa como verdadera.

Cada uno de estos conceptos se describe con más detalle a continuación.

Realtime Database

En Realtime Database, las reglas de seguridad de Firebase son expresiones similares a las de JavaScript, contenidas en un documento JSON.

Usan la siguiente sintaxis:

{
  "rules": {
    "<<path>>": {
    // Allow the request if the condition for each method is true.
      ".read": <<condition>>,
      ".write": <<condition>>,
      ".validate": <<condition>>
    }
  }
}

Hay tres elementos básicos en la regla:

  • Ruta: La ubicación de la base de datos. Esto duplica la estructura JSON de tu base de datos.
  • Solicitud: Estos son los métodos que usa la regla para otorgar acceso. Las reglas read y write otorgan acceso amplio de lectura y escritura, mientras que las reglas validate actúan como verificación secundaria para otorgar acceso según los datos entrantes o existentes.
  • Condición: La condición que permite una solicitud si se evalúa como verdadera.

Construcciones de las reglas

Cloud Firestore

Los elementos básicos de una regla en Cloud Firestore y Cloud Storage son los siguientes:

  • La declaración service declara el producto de Firebase al que se aplican las reglas.
  • El bloque match define una ruta en la base de datos o en el depósito de almacenamiento al que se aplican las reglas.
  • La declaración allow proporciona condiciones para otorgar acceso, diferenciadas por métodos. Los métodos compatibles son get, list, create, update y delete, y los métodos de conveniencia son read y write.
  • Las declaraciones function opcionales proporcionan la capacidad de combinar y unir condiciones para su uso en varias reglas.

El service contiene uno o más bloques match con declaraciones allow que proporcionan condiciones que otorgan acceso a las solicitudes. Las variables request y resource están disponibles para su uso en condiciones de reglas. El lenguaje de las reglas de seguridad de Firebase también admite declaraciones function.

Versión de sintaxis

La declaración syntax indica la versión del lenguaje de reglas de Firebase que se usa para escribir el origen. La última versión del lenguaje es v2.

rules_version = '2';
service cloud.firestore {
...
}

Si no se proporciona una declaración rules_version, las reglas se evaluarán con el motor v1.

Servicio

La declaración service define a qué producto o servicio de Firebase se aplican tus reglas. Solo puedes incluir una declaración service por archivo de origen.

Cloud Firestore

service cloud.firestore {
 // Your 'match' blocks with their corresponding 'allow' statements and
 // optional 'function' declarations are contained here
}

Cloud Storage

service firebase.storage {
  // Your 'match' blocks with their corresponding 'allow' statements and
  // optional 'function' declarations are contained here
}

Si defines reglas para Cloud Firestore y Cloud Storage mediante Firebase CLI, deberás mantenerlas en archivos independientes.

Match

Un bloque match declara un patrón path que se compara con la ruta para la operación solicitada (la request.path entrante). El cuerpo de match debe tener uno o más bloques match anidados, declaraciones allow o declaraciones function. La ruta de los bloques match anidados es relativa a la del bloque match superior.

El patrón path es un nombre similar a un directorio que puede incluir variables o comodines. El patrón path permite coincidencias de segmento de ruta única y de varias rutas. Cualquier variable vinculada en un path es visible dentro del alcance de match o de cualquier alcance anidado en el que se declare path.

Las coincidencias con un patrón path pueden ser parciales o completas:

  • Coincidencias parciales: El patrón path es una coincidencia de prefijo de request.path.
  • Coincidencias completas: El patrón path coincide con toda la request.path.

Cuando se realiza una coincidencia completa, se evalúan las reglas dentro del bloque. Cuando se obtiene una coincidencia parcial, se evalúan las reglas match anidadas para ver si alguna path anidada completará la coincidencia.

Las reglas en cada match completa se evalúan para permitir o no la solicitud. Si alguna regla coincidente otorga acceso, se permite la solicitud. Si ninguna regla coincidente otorga acceso, se deniega la solicitud.

// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
  // Partial match.
  match /example/{singleSegment} {   // `singleSegment` == 'hello'
    allow write;                     // Write rule not evaluated.
    // Complete match.
    match /nested/path {             // `singleSegment` visible in scope.
      allow read;                    // Read rule is evaluated.
    }
  }
  // Complete match.
  match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
    allow read;                      // Read rule is evaluated.
  }
}

Como se muestra en el ejemplo anterior, las declaraciones path admiten las siguientes variables:

  • Comodín en segmento único: una variable de comodín se declara en una ruta mediante la unión de una variable entre llaves: {variable}. Esta variable es accesible dentro de la declaración match como una string.
  • Comodín recurrente: el comodín recurrente, o de varios segmentos, coincide con varios segmentos de la ruta de acceso en una ruta o debajo de ella. Este comodín coincide con todas las rutas debajo de la ubicación donde lo configuraste. Para declararlo, agrega la string =** al final de tu variable de segmento: {variable=**}. Esta variable es accesible en la declaración match como un objeto path.

Allow

El bloque match contiene una o más declaraciones allow. Estas son tus reglas reales. Puedes aplicar reglas allow a uno o más métodos. Las condiciones de una declaración allow se deben evaluar como verdaderas para que Cloud Firestore o Cloud Storage otorguen acceso a cualquier solicitud entrante. También puedes escribir declaraciones allow sin condiciones; por ejemplo, allow read. Sin embargo, si la declaración allow no incluye una condición, siempre permitirá la solicitud para ese método.

Si se cumple alguna de las reglas allow para el método, se permite la solicitud. Además, si una regla más amplia otorga acceso, las reglas lo otorgan y, además, ignoran cualquier otra regla detallada que pueda limitar el acceso.

Considera el siguiente ejemplo, en el que cualquier usuario puede leer o borrar cualquiera de sus propios archivos. Una regla más detallada solo permite escrituras si el usuario que solicita la escritura es el propietario del archivo y es un archivo PNG. Un usuario puede borrar cualquier archivo en la ruta secundaria, incluso si no es PNG, ya que la regla anterior lo permite.

service firebase.storage {
  // Allow the requestor to read or delete any resource on a path under the
  // user directory.
  match /users/{userId}/{anyUserFile=**} {
    allow read, delete: if request.auth != null && request.auth.uid == userId;
  }

  // Allow the requestor to create or update their own images.
  // When 'request.method' == 'delete' this rule and the one matching
  // any path under the user directory would both match and the `delete`
  // would be permitted.

  match /users/{userId}/images/{imageId} {
    // Whether to permit the request depends on the logical OR of all
    // matched rules. This means that even if this rule did not explicitly
    // allow the 'delete' the earlier rule would have.
    allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
  }
}

Método

Cada declaración allow incluye un método que otorga acceso a las solicitudes entrantes del mismo método.

Método Tipo de solicitud
Métodos de conveniencia
read Cualquier tipo de solicitud de lectura
write Cualquier tipo de solicitud de escritura
Métodos estándar
get Solicitudes de lectura de documentos o archivos individuales
list Solicitudes de lectura para búsquedas y colecciones
create Escribir documentos o archivos nuevos
update Escribir en documentos de bases de datos existentes o actualizar metadatos de archivos
delete Borra datos

No puedes superponer métodos de lectura en el mismo bloque match o métodos de escritura en conflicto en la misma declaración path.

Por ejemplo, las siguientes reglas fallarían:

service bad.example {
  match /rules/with/overlapping/methods {
    // This rule allows reads to all authenticated users
    allow read: if request.auth != null;

    match another/subpath {
      // This secondary, more specific read rule causes an error
      allow get: if request.auth != null && request.auth.uid == "me";
      // Overlapping write methods in the same path cause an error as well
      allow write: if request.auth != null;
      allow create: if request.auth != null && request.auth.uid == "me";
    }
  }
}

Función

A medida que tus reglas de seguridad se vuelvan más complejas, te recomendamos agrupar conjuntos de condiciones en funciones que puedas volver a usar en tu conjunto de reglas. Las reglas de seguridad admiten funciones personalizadas. La sintaxis de las funciones personalizadas es similar a la de JavaScript, pero las funciones de reglas de seguridad se escriben en un lenguaje específico del dominio y tiene algunas limitaciones importantes:

  • Las funciones solo pueden incluir una sola declaración return. No pueden contener lógica adicional. Por ejemplo, no pueden ejecutar bucles ni llamar a servicios externos.
  • Las funciones pueden acceder automáticamente a funciones y variables desde el permiso en el que se definen. Por ejemplo, una función definida en el alcance service cloud.firestore tiene acceso a la variable resource y a las funciones integradas, como get() y exists().
  • Las funciones pueden llamar a otras funciones, pero no hacer referencia a sí mismas. La profundidad total de la pila de llamadas se limita a 20.
  • En la versión de reglas v2, las funciones pueden definir variables con la palabra clave let. Las funciones pueden tener hasta 10 vinculaciones let, pero deben terminar con una declaración de devolución.

Una función se define con la palabra clave function y usa cero o más argumentos. Por ejemplo, puedes combinar los dos tipos de condiciones usados en los ejemplos anteriores en una única función:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

A continuación, se muestra un ejemplo que incluye argumentos de funciones y asignaciones let. Las declaraciones de asignaciones let se deben separar con punto y coma.

function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
  return isAuthor || isAdmin;
}

Observa cómo la asignación isAdmin aplica una búsqueda de la colección de administradores. Para una evaluación diferida que no requiere búsquedas innecesarias, aprovecha la naturaleza de cortocircuito de las comparaciones && (Y) y || (O) para llamar a una segunda función solo si isAuthor se muestra como verdadero (para comparaciones &&) o falso (para comparaciones ||).

function isAdmin(userId) {
  return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  // `||` is short-circuiting; isAdmin called only if isAuthor == false.
  return isAuthor || isAdmin(userId);
}

Usar funciones en las reglas de seguridad facilita su mantenimiento a medida que aumenta su complejidad.

Cloud Storage

Los elementos básicos de una regla en Cloud Firestore y Cloud Storage son los siguientes:

  • La declaración service declara el producto de Firebase al que se aplican las reglas.
  • El bloque match define una ruta en la base de datos o en el depósito de almacenamiento al que se aplican las reglas.
  • La declaración allow proporciona condiciones para otorgar acceso, diferenciadas por métodos. Los métodos compatibles son get, list, create, update y delete, y los métodos de conveniencia son read y write.
  • Las declaraciones function opcionales proporcionan la capacidad de combinar y unir condiciones para su uso en varias reglas.

El service contiene uno o más bloques match con declaraciones allow que proporcionan condiciones que otorgan acceso a las solicitudes. Las variables request y resource están disponibles para su uso en condiciones de reglas. El lenguaje de las reglas de seguridad de Firebase también admite declaraciones function.

Versión de sintaxis

La declaración syntax indica la versión del lenguaje de reglas de Firebase que se usa para escribir el origen. La última versión del lenguaje es v2.

rules_version = '2';
service cloud.firestore {
...
}

Si no se proporciona una declaración rules_version, las reglas se evaluarán con el motor v1.

Servicio

La declaración service define a qué producto o servicio de Firebase se aplican tus reglas. Solo puedes incluir una declaración service por archivo de origen.

Cloud Firestore

service cloud.firestore {
 // Your 'match' blocks with their corresponding 'allow' statements and
 // optional 'function' declarations are contained here
}

Cloud Storage

service firebase.storage {
  // Your 'match' blocks with their corresponding 'allow' statements and
  // optional 'function' declarations are contained here
}

Si defines reglas para Cloud Firestore y Cloud Storage mediante Firebase CLI, deberás mantenerlas en archivos independientes.

Match

Un bloque match declara un patrón path que se compara con la ruta para la operación solicitada (la request.path entrante). El cuerpo de match debe tener uno o más bloques match anidados, declaraciones allow o declaraciones function. La ruta de los bloques match anidados es relativa a la del bloque match superior.

El patrón path es un nombre similar a un directorio que puede incluir variables o comodines. El patrón path permite coincidencias de segmento de ruta única y de varias rutas. Cualquier variable vinculada en un path es visible dentro del alcance de match o de cualquier alcance anidado en el que se declare path.

Las coincidencias con un patrón path pueden ser parciales o completas:

  • Coincidencias parciales: El patrón path es una coincidencia de prefijo de request.path.
  • Coincidencias completas: El patrón path coincide con toda la request.path.

Cuando se realiza una coincidencia completa, se evalúan las reglas dentro del bloque. Cuando se obtiene una coincidencia parcial, se evalúan las reglas match anidadas para ver si alguna path anidada completará la coincidencia.

Las reglas en cada match completa se evalúan para permitir o no la solicitud. Si alguna regla coincidente otorga acceso, se permite la solicitud. Si ninguna regla coincidente otorga acceso, se deniega la solicitud.

// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
  // Partial match.
  match /example/{singleSegment} {   // `singleSegment` == 'hello'
    allow write;                     // Write rule not evaluated.
    // Complete match.
    match /nested/path {             // `singleSegment` visible in scope.
      allow read;                    // Read rule is evaluated.
    }
  }
  // Complete match.
  match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
    allow read;                      // Read rule is evaluated.
  }
}

Como se muestra en el ejemplo anterior, las declaraciones path admiten las siguientes variables:

  • Comodín en segmento único: una variable de comodín se declara en una ruta mediante la unión de una variable entre llaves: {variable}. Esta variable es accesible dentro de la declaración match como una string.
  • Comodín recurrente: el comodín recurrente, o de varios segmentos, coincide con varios segmentos de la ruta de acceso en una ruta o debajo de ella. Este comodín coincide con todas las rutas debajo de la ubicación donde lo configuraste. Para declararlo, agrega la string =** al final de tu variable de segmento: {variable=**}. Esta variable es accesible en la declaración match como un objeto path.

Allow

El bloque match contiene una o más declaraciones allow. Estas son tus reglas reales. Puedes aplicar reglas allow a uno o más métodos. Las condiciones de una declaración allow se deben evaluar como verdaderas para que Cloud Firestore o Cloud Storage otorguen acceso a cualquier solicitud entrante. También puedes escribir declaraciones allow sin condiciones; por ejemplo, allow read. Sin embargo, si la declaración allow no incluye una condición, siempre permitirá la solicitud para ese método.

Si se cumple alguna de las reglas allow para el método, se permite la solicitud. Además, si una regla más amplia otorga acceso, las reglas lo otorgan y, además, ignoran cualquier otra regla detallada que pueda limitar el acceso.

Considera el siguiente ejemplo, en el que cualquier usuario puede leer o borrar cualquiera de sus propios archivos. Una regla más detallada solo permite escrituras si el usuario que solicita la escritura es el propietario del archivo y es un archivo PNG. Un usuario puede borrar cualquier archivo en la ruta secundaria, incluso si no es PNG, ya que la regla anterior lo permite.

service firebase.storage {
  // Allow the requestor to read or delete any resource on a path under the
  // user directory.
  match /users/{userId}/{anyUserFile=**} {
    allow read, delete: if request.auth != null && request.auth.uid == userId;
  }

  // Allow the requestor to create or update their own images.
  // When 'request.method' == 'delete' this rule and the one matching
  // any path under the user directory would both match and the `delete`
  // would be permitted.

  match /users/{userId}/images/{imageId} {
    // Whether to permit the request depends on the logical OR of all
    // matched rules. This means that even if this rule did not explicitly
    // allow the 'delete' the earlier rule would have.
    allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
  }
}

Método

Cada declaración allow incluye un método que otorga acceso a las solicitudes entrantes del mismo método.

Método Tipo de solicitud
Métodos de conveniencia
read Cualquier tipo de solicitud de lectura
write Cualquier tipo de solicitud de escritura
Métodos estándar
get Solicitudes de lectura de documentos o archivos individuales
list Solicitudes de lectura para búsquedas y colecciones
create Escribir documentos o archivos nuevos
update Escribir en documentos de bases de datos existentes o actualizar metadatos de archivos
delete Borra datos

No puedes superponer métodos de lectura en el mismo bloque match o métodos de escritura en conflicto en la misma declaración path.

Por ejemplo, las siguientes reglas fallarían:

service bad.example {
  match /rules/with/overlapping/methods {
    // This rule allows reads to all authenticated users
    allow read: if request.auth != null;

    match another/subpath {
      // This secondary, more specific read rule causes an error
      allow get: if request.auth != null && request.auth.uid == "me";
      // Overlapping write methods in the same path cause an error as well
      allow write: if request.auth != null;
      allow create: if request.auth != null && request.auth.uid == "me";
    }
  }
}

Función

A medida que tus reglas de seguridad se vuelvan más complejas, te recomendamos agrupar conjuntos de condiciones en funciones que puedas volver a usar en tu conjunto de reglas. Las reglas de seguridad admiten funciones personalizadas. La sintaxis de las funciones personalizadas es similar a la de JavaScript, pero las funciones de reglas de seguridad se escriben en un lenguaje específico del dominio y tiene algunas limitaciones importantes:

  • Las funciones solo pueden incluir una sola declaración return. No pueden contener lógica adicional. Por ejemplo, no pueden ejecutar bucles ni llamar a servicios externos.
  • Las funciones pueden acceder automáticamente a funciones y variables desde el permiso en el que se definen. Por ejemplo, una función definida en el alcance service cloud.firestore tiene acceso a la variable resource y a las funciones integradas, como get() y exists().
  • Las funciones pueden llamar a otras funciones, pero no hacer referencia a sí mismas. La profundidad total de la pila de llamadas se limita a 20.
  • En la versión de reglas v2, las funciones pueden definir variables con la palabra clave let. Las funciones pueden tener hasta 10 vinculaciones let, pero deben terminar con una declaración de devolución.

Una función se define con la palabra clave function y usa cero o más argumentos. Por ejemplo, puedes combinar los dos tipos de condiciones usados en los ejemplos anteriores en una única función:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

A continuación, se muestra un ejemplo que incluye argumentos de funciones y asignaciones let. Las declaraciones de asignaciones let se deben separar con punto y coma.

function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
  return isAuthor || isAdmin;
}

Observa cómo la asignación isAdmin aplica una búsqueda de la colección de administradores. Para una evaluación diferida que no requiere búsquedas innecesarias, aprovecha la naturaleza de cortocircuito de las comparaciones && (Y) y || (O) para llamar a una segunda función solo si isAuthor se muestra como verdadero (para comparaciones &&) o falso (para comparaciones ||).

function isAdmin(userId) {
  return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  // `||` is short-circuiting; isAdmin called only if isAuthor == false.
  return isAuthor || isAdmin(userId);
}

Usar funciones en las reglas de seguridad facilita su mantenimiento a medida que aumenta su complejidad.

Realtime Database

Como se describió anteriormente, las reglas de Realtime Database incluyen tres elementos básicos: la ubicación de la base de datos como una duplicación de la estructura JSON de la base de datos, el tipo de solicitud y la condición que otorga el acceso.

Ubicación de la base de datos

La estructura de tus reglas debe seguir la de los datos que almacenaste en tu base de datos. Por ejemplo, en una app de chat con una lista de mensajes, es posible que tengas datos que se parezcan a los siguientes:

  {
    "messages": {
      "message0": {
        "content": "Hello",
        "timestamp": 1405704370369
      },
      "message1": {
        "content": "Goodbye",
        "timestamp": 1405704395231
      },
      ...
    }
  }

Tus reglas deben duplicar esa estructura. Por ejemplo:

  {
    "rules": {
      "messages": {
        "$message": {
          // only messages from the last ten minutes can be read
          ".read": "data.child('timestamp').val() > (now - 600000)",

          // new messages must have a string content and a number timestamp
          ".validate": "newData.hasChildren(['content', 'timestamp']) &&
                        newData.child('content').isString() &&
                        newData.child('timestamp').isNumber()"
        }
      }
    }
  }

Como se muestra en el ejemplo anterior, las reglas de Realtime Database admiten una variable $location que coincida con segmentos de la ruta de acceso. Usa el prefijo $ delante del segmento de la ruta de acceso para hacer coincidir tu regla con los nodos secundarios en la ruta.

  {
    "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')"
          }
        }
      }
    }
  }

También puedes usar $variable en paralelo con nombres de ruta de acceso constantes.

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

Método

En Realtime Database, hay tres tipos de reglas. Dos de estos tipos de reglas (read y write) se aplican al método de una solicitud entrante. El tipo de regla validate aplica estructuras de datos y valida el formato y el contenido de los datos. Las reglas ejecutan reglas .validate después de verificar que una regla .write otorga acceso.

Tipos de reglas
.read Describe si los usuarios pueden leer los datos y cuándo pueden hacerlo.
.write Indica si se permite la escritura de datos y en qué momento se permite.
.validate Define el aspecto de un valor con formato correcto, si este tiene atributos secundarios y el tipo de datos.

De acuerdo con la configuración predeterminada, si no hay una regla que lo permita, se rechaza el acceso a la ruta de acceso.

Cómo compilar condiciones

Cloud Firestore

Una condición es una expresión booleana que determina si se debe permitir o rechazar una operación en particular. Las variables request y resource proporcionan contexto para esas condiciones.

La variable request

La variable request incluye los siguientes campos y la información correspondiente:

request.auth

Un token web JSON (JWT) que contiene credenciales de autenticación de Firebase Authentication. El token auth contiene un conjunto de reclamaciones estándar y cualquier reclamación personalizada que crees a través de Firebase Authentication. Obtén más información sobre las reglas de seguridad y Firebase Authentication.

request.method

request.method puede ser cualquiera de los métodos estándar o uno personalizado. Los métodos de conveniencia read y write también existen para simplificar las reglas de escritura que se aplican a todos los métodos estándar de solo lectura o solo escritura, respectivamente.

request.params

request.params incluye cualquier dato no relacionado específicamente con el request.resource que podría ser útil para la evaluación. En la práctica, este mapa debe estar vacío para todos los métodos estándar y debe contener datos que no sean de recursos de métodos personalizados. En los servicios se debe tener cuidado de no cambiar el nombre ni modificar el tipo de cualquiera de las claves y los valores presentados como parámetros.

request.path

request.path es la ruta para el resource de destino. Esta ruta es relativa al servicio. Los segmentos de ruta que contienen caracteres seguros que no son URL, como /, están codificados en URL.

La variable resource

resource es el valor actual en el servicio representado como un mapa de pares clave-valor. Hacer referencia a resource en una condición generará, como máximo, una lectura del valor del servicio. Esta búsqueda se contará en cualquier cuota del recurso relacionada con el servicio. Para las solicitudes get, resource solo se considerará en la cuota si se deniega.

Operadores y prioridad de operadores

Usa la tabla que está a continuación como referencia para los operadores y su prioridad correspondiente en las reglas de Cloud Firestore y Cloud Storage.

Dadas las expresiones arbitrarias a y b, un campo f y un índice i.

Operador Descripción Asociatividad
a[i] a() a.f Acceso a índice, llamada o campo de izquierda a derecha
!a -a Negación unaria De derecha a izquierda
a/b a%b a*b Operadores multiplicativos de izquierda a derecha
a+b a-b Operadores aditivos de izquierda a derecha
a>b a>=b a<=b Operadores relacionales de izquierda a derecha
a in b Existencia en lista o mapa de izquierda a derecha
a is type Comparación de tipos, en la que type puede ser bool, int, float, number, string, list, map, timestamp, duration, path o latlng de izquierda a derecha
a==b a!=b Operadores de comparación de izquierda a derecha
a && b Condicional AND de izquierda a derecha
a || b Condicional OR de izquierda a derecha
a ? true_value : false_value Expresión ternaria de izquierda a derecha

Cloud Storage

Una condición es una expresión booleana que determina si se debe permitir o rechazar una operación en particular. Las variables request y resource proporcionan contexto para esas condiciones.

La variable request

La variable request incluye los siguientes campos y la información correspondiente:

request.auth

Un token web JSON (JWT) que contiene credenciales de autenticación de Firebase Authentication. El token auth contiene un conjunto de reclamaciones estándar y cualquier reclamación personalizada que crees a través de Firebase Authentication. Obtén más información sobre las reglas de seguridad y Firebase Authentication.

request.method

request.method puede ser cualquiera de los métodos estándar o uno personalizado. Los métodos de conveniencia read y write también existen para simplificar las reglas de escritura que se aplican a todos los métodos estándar de solo lectura o solo escritura, respectivamente.

request.params

request.params incluye cualquier dato no relacionado específicamente con el request.resource que podría ser útil para la evaluación. En la práctica, este mapa debe estar vacío para todos los métodos estándar y debe contener datos que no sean de recursos de métodos personalizados. En los servicios se debe tener cuidado de no cambiar el nombre ni modificar el tipo de cualquiera de las claves y los valores presentados como parámetros.

request.path

request.path es la ruta para el resource de destino. Esta ruta es relativa al servicio. Los segmentos de ruta que contienen caracteres seguros que no son URL, como /, están codificados en URL.

La variable resource

resource es el valor actual en el servicio representado como un mapa de pares clave-valor. Hacer referencia a resource en una condición generará, como máximo, una lectura del valor del servicio. Esta búsqueda se contará en cualquier cuota del recurso relacionada con el servicio. Para las solicitudes get, resource solo se considerará en la cuota si se deniega.

Operadores y prioridad de operadores

Usa la tabla que está a continuación como referencia para los operadores y su prioridad correspondiente en las reglas de Cloud Firestore y Cloud Storage.

Dadas las expresiones arbitrarias a y b, un campo f y un índice i.

Operador Descripción Asociatividad
a[i] a() a.f Acceso a índice, llamada o campo de izquierda a derecha
!a -a Negación unaria De derecha a izquierda
a/b a%b a*b Operadores multiplicativos de izquierda a derecha
a+b a-b Operadores aditivos de izquierda a derecha
a>b a>=b a<=b Operadores relacionales de izquierda a derecha
a in b Existencia en lista o mapa de izquierda a derecha
a is type Comparación de tipos, en la que type puede ser bool, int, float, number, string, list, map, timestamp, duration, path o latlng de izquierda a derecha
a==b a!=b Operadores de comparación de izquierda a derecha
a && b Condicional AND de izquierda a derecha
a || b Condicional OR de izquierda a derecha
a ? true_value : false_value Expresión ternaria de izquierda a derecha

Realtime Database

Una condición es una expresión booleana que determina si se debe permitir o rechazar una operación en particular. Puedes definir esas condiciones en las reglas de Realtime Database de las siguientes maneras.

Variables predefinidas

Hay una serie de variables predefinidas útiles a las que se puede acceder dentro de una definición de reglas. A continuación, se incluye un breve resumen de cada una:

Variables predefinidas
now La hora actual en milisegundos desde la época Linux. Esto funciona particularmente bien para validar las marcas de tiempo creadas con firebase.database.ServerValue.TIMESTAMP del SDK.
root Una RuleDataSnapshot que representa la ruta de acceso de raíz en la base de datos de Firebase, tal como existe antes de la operación que se intenta ejecutar.
newData Una RuleDataSnapshot que representa los datos como existirían después de la operación que se intenta ejecutar. Incluye los datos nuevos que se escriben y los datos existentes.
data Una RuleDataSnapshot que representa los datos como existían antes de la operación que se intenta ejecutar.
Variables $ Ruta de acceso de comodín que se usa para representar los ID y las claves secundarias dinámicas.
auth Representa la carga útil del token de un usuario autenticado.

Estas variables pueden usarse en cualquier sección de tus reglas. Por ejemplo, las siguientes reglas de seguridad garantizan que los datos escritos en el nodo /foo/ deben ser una string de menos de 100 caracteres:

{
  "rules": {
    "foo": {
      // /foo is readable by the world
      ".read": true,

      // /foo is writable by the world
      ".write": true,

      // data written to /foo must be a string less than 100 characters
      ".validate": "newData.isString() && newData.val().length < 100"
    }
  }
}

Reglas basadas en datos

Cualquier dato en tu base de datos se puede usar en tus reglas. Con las variables predefinidas root, data y newData, puedes acceder a cualquier ruta como existiría antes o después de un evento de escritura.

Considera este ejemplo, que permite operaciones de escritura siempre que el valor del nodo /allow_writes/ sea true, el nodo principal no tenga una marca readOnly y haya un elemento secundario llamado foo en los datos recién escritos:

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

Reglas basadas en búsquedas

Si bien no puedes usar reglas como filtros, puedes limitar el acceso a subconjuntos de datos mediante el uso de parámetros de consulta en tus reglas. Usa expresiones query. en tus reglas para otorgar acceso de lectura o escritura en función de los parámetros de consulta.

Por ejemplo, la siguiente regla basada en consultas usa reglas de seguridad basadas en usuarios y en consultas para restringir el acceso a los datos de la colección baskets solo a los carritos de compra que posee el usuario activo:

"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, se ejecutaría correctamente:

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

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

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

Además, puedes usar reglas basadas en consultas para limitar la cantidad de datos que un cliente descarga mediante operaciones de lectura.

Por ejemplo, la siguiente regla limita el acceso de lectura a los primeros 1,000 resultados de una consulta únicamente, los cuales se ordenan 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)

Las siguientes expresiones query. están disponibles en las reglas de Realtime Database.

Expresiones de reglas basadas en consultas
Expresión Tipo Descripción
query.orderByKey
query.orderByPriority
query.orderByValue
booleana Verdadera para consultas ordenadas según la clave, la prioridad o el valor. De lo contrario, el valor el falso.
query.orderByChild string
nula
Usa una string para representar la ruta de acceso relativa a un nodo secundario. Por ejemplo, query.orderByChild == "address/zip". Si la consulta no se ordena según un nodo secundario, este valor es nulo.
query.startAt
query.endAt
query.equalTo
string
número
booleano
nulo
Recupera los límites de la consulta en ejecución o muestra el valor nulo si no se estableció ningún límite.
query.limitToFirst
query.limitToLast
número
nula
Recupera el límite de la consulta en ejecución o muestra el valor nulo si no se estableció ningún límite.

Operadores

Las reglas de Realtime Database admiten una cantidad de operadores que puedes usar para combinar variables en la declaración de condición. Consulta la lista completa de operadores en la documentación de referencia.

Cómo crear condiciones

Tus condiciones reales variarán según el acceso que desees otorgar. Las reglas ofrecen intencionalmente un enorme grado de flexibilidad, por lo que las reglas de tu app pueden ser tan simples o complejas como necesites.

Si deseas obtener información sobre cómo crear reglas simples y listas para la producción, consulta las reglas básicas de seguridad.