Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Cómo funcionan las reglas de seguridad

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

La seguridad puede ser una de las piezas más complejas del rompecabezas del desarrollo de aplicaciones. En la mayoría de las aplicaciones, los desarrolladores deben crear y ejecutar un servidor que maneje la autenticación (quién es un usuario) y la autorización (lo que puede hacer un usuario).

Las reglas de seguridad de Firebase eliminan la capa intermedia (servidor) y le permiten especificar permisos basados ​​en rutas para los clientes que se conectan a sus datos directamente. Utilice esta guía para obtener más información sobre cómo se aplican las reglas a las solicitudes entrantes.

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

Tienda de fuego en la nube

Estructura basica

Las reglas de seguridad de Firebase en Cloud Firestore y Cloud Storage usan 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 a medida que crea las reglas:

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

Reglas de seguridad versión 2

A partir de mayo de 2019, ya está disponible la versión 2 de las reglas de seguridad de Firebase. La versión 2 de las reglas cambia el comportamiento de los comodines recursivos {name=**} . Debe utilizar la versión 2 si planea utilizar consultas de grupos de recopilación . Debe optar por la versión 2 haciendo rules_version = '2'; la primera línea en sus reglas de seguridad:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

Rutas coincidentes

Todas las declaraciones de coincidencia deben apuntar a documentos, no a colecciones. Una declaración de coincidencia puede apuntar a un documento específico, como en match /cities/SF o usar comodines para apuntar a cualquier documento en la ruta especificada, como en match /cities/{city} .

En el ejemplo anterior, la declaración de coincidencia utiliza la sintaxis de comodín {city} . Esto significa que la regla se aplica a cualquier documento de la colección de cities , como /cities/SF o /cities/NYC . Cuando se evalúan las expresiones de allow en la declaración de coincidencia, la variable de la city se resolverá en el nombre del documento de la ciudad, como SF o NYC .

Subcolecciones coincidentes

Los datos en Cloud Firestore están organizados en colecciones de documentos, y cada documento puede extender la jerarquía a través de subcolecciones. Es importante comprender cómo interactúan las reglas de seguridad con los datos jerárquicos.

Considere la situación en la que cada documento de la colección de cities contiene una subcolección de landmarks de referencia. Las reglas de seguridad se aplican solo en la ruta coincidente, por lo que los controles de acceso definidos en la colección de cities no se aplican a la subcolección de landmarks de referencia. En su lugar, escribe reglas explícitas para controlar el acceso a las subcolecciones:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read, write: if <condition>;

      // Explicitly define rules for the 'landmarks' subcollection
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}

Al anidar sentencias de match , la ruta de la sentencia de match interna siempre es relativa a la ruta de la sentencia de match externa. Por lo tanto, los siguientes conjuntos de reglas son equivalentes:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}
service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city}/landmarks/{landmark} {
      allow read, write: if <condition>;
    }
  }
}

Comodines recursivos

Si desea que las reglas se apliquen a una jerarquía arbitrariamente profunda, use la sintaxis de comodines recursivos, {name=**} :

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

Cuando se usa la sintaxis de comodín recursivo, la variable comodín contendrá el segmento de ruta coincidente completo, incluso si el documento se encuentra en una subcolección profundamente anidada. Por ejemplo, las reglas enumeradas anteriormente coincidirían con un documento ubicado en /cities/SF/landmarks/coit_tower y el valor de la variable del document sería SF/landmarks/coit_tower .

Tenga en cuenta, sin embargo, que el comportamiento de los comodines recursivos depende de la versión de las reglas.

Versión 1

Las reglas de seguridad utilizan la versión 1 de forma predeterminada. En la versión 1, los comodines recursivos coinciden con uno o más elementos de ruta. No coinciden con una ruta vacía, por lo que match /cities/{city}/{document=**} coincide con documentos en subcolecciones pero no en la colección de cities , mientras que match /cities/{document=**} coincide con ambos documentos en el colección y subcolecciones de cities .

Los comodines recursivos deben aparecer al final de una declaración de coincidencia.

Versión 2

En la versión 2 de las reglas de seguridad, los comodines recursivos coinciden con cero o más elementos de ruta. match/cities/{city}/{document=**} coincide con los documentos de cualquier subcoleccion, así como con los documentos de la colección de cities .

Debe optar por la versión 2 agregando rules_version = '2'; en la parte superior de sus reglas de seguridad:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{city}/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

Puede tener como máximo un comodín recursivo por declaración de coincidencia, pero en la versión 2, puede colocar este comodín en cualquier parte de la declaración de coincidencia. Por ejemplo:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the songs collection group
    match /{path=**}/songs/{song} {
      allow read, write: if <condition>;
    }
  }
}

Si utiliza consultas de grupos de recopilación , debe utilizar la versión 2; consulte protección de consultas de grupos de recopilación .

Declaraciones de coincidencia superpuestas

Es posible que un documento coincida con más de una declaración de match . En el caso de que varias expresiones de allow coincidan con una solicitud, se permite el acceso si se true alguna de las condiciones:

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the 'cities' collection.
    match /cities/{city} {
      allow read, write: if false;
    }

    // Matches any document in the 'cities' collection or subcollections.
    match /cities/{document=**} {
      allow read, write: if true;
    }
  }
}

En el ejemplo anterior, se permitirán todas las lecturas y escrituras en la colección de cities porque la segunda regla siempre es true , aunque la primera regla siempre es false .

Límites de las reglas de seguridad

Cuando trabaje con reglas de seguridad, tenga en cuenta los siguientes límites:

Límite Detalles
Número máximo de llamadas a exist( exists() , get() y getAfter() por solicitud
  • 10 para solicitudes de un solo documento y solicitudes de consulta.
  • 20 para lecturas, transacciones y escrituras por lotes de múltiples documentos. El límite anterior de 10 también se aplica a cada operación.

    Por ejemplo, imagine que crea una solicitud de escritura por lotes con 3 operaciones de escritura y que sus reglas de seguridad usan 2 llamadas de acceso a documentos para validar cada escritura. En este caso, cada escritura usa 2 de sus 10 llamadas de acceso y la solicitud de escritura por lotes usa 6 de sus 20 llamadas de acceso.

Exceder cualquiera de los límites da como resultado un error de permiso denegado.

Algunas llamadas de acceso a documentos pueden almacenarse en caché y las llamadas almacenadas en caché no cuentan para los límites.

Profundidad máxima de declaración match anidada 10
Longitud máxima de ruta, en segmentos de ruta, permitida dentro de un conjunto de sentencias de match anidadas 100
Número máximo de variables de captura de ruta permitidas dentro de un conjunto de declaraciones de match anidadas 20
Profundidad máxima de llamada de función 20
Número máximo de argumentos de función 7
Número máximo de enlaces de variables let por función 10
Número máximo de llamadas a funciones recursivas o cíclicas 0 (no permitido)
Número máximo de expresiones evaluadas por solicitud 1,000
Tamaño máximo de un conjunto de reglas Los conjuntos de reglas deben obedecer dos límites de tamaño:
  • un límite de 256 KB en el tamaño de la fuente de texto del conjunto de reglas publicado desde Firebase console o desde la CLI mediante firebase deploy .
  • un límite de 250 KB en el tamaño del conjunto de reglas compilado que resulta cuando Firebase procesa la fuente y la activa en el back-end.

Almacenamiento en la nube

Estructura basica

Las reglas de seguridad de Firebase en Cloud Firestore y Cloud Storage usan 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 a medida que crea las reglas:

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

Rutas coincidentes

Las reglas de seguridad de Cloud Storage match las rutas de archivo utilizadas para acceder a los archivos en Cloud Storage. Las reglas pueden match rutas exactas o rutas comodín, y las reglas también se pueden anidar. Si ninguna regla de coincidencia permite un método de solicitud, o si la condición se evalúa como false , se deniega la solicitud.

Coincidencias exactas

// Exact match for "images/profilePhoto.png"
match /images/profilePhoto.png {
  allow write: if <condition>;
}

// Exact match for "images/croppedProfilePhoto.png"
match /images/croppedProfilePhoto.png {
  allow write: if <other_condition>;
}

Coincidencias anidadas

// Partial match for files that start with "images"
match /images {
  // Exact match for "images/profilePhoto.png"
  match /profilePhoto.png {
    allow write: if <condition>;
  }

  // Exact match for "images/croppedProfilePhoto.png"
  match /croppedProfilePhoto.png {
    allow write: if <other_condition>;
  }
}

Coincidencias comodín

Las reglas también se pueden usar para hacer match un patrón usando comodines. Un comodín es una variable con nombre que representa una sola cadena, como profilePhoto.png , o varios segmentos de ruta, como images/profilePhoto.png .

Un comodín se crea agregando llaves alrededor del nombre del comodín, como {string} . Se puede declarar un comodín de segmento múltiple agregando =** al nombre del comodín, como {path=**} :

// Partial match for files that start with "images"
match /images {
  // Exact match for "images/*"
  // e.g. images/profilePhoto.png is matched
  match /{imageId} {
    // This rule only matches a single path segment (*)
    // imageId is a string that contains the specific segment matched
    allow read: if <condition>;
  }

  // Exact match for "images/**"
  // e.g. images/users/user:12345/profilePhoto.png is matched
  // images/profilePhoto.png is also matched!
  match /{allImages=**} {
    // This rule matches one or more path segments (**)
    // allImages is a path that contains all segments matched
    allow read: if <other_condition>;
  }
}

Si varias reglas coinciden con un archivo, el resultado es el OR del resultado de todas las evaluaciones de reglas. Es decir, si alguna regla con la que coincida el archivo se evalúa como true , el resultado es true .

En las reglas anteriores, el archivo "images/profilePhoto.png" se puede leer si la condition u other_condition se evalúa como verdadera, mientras que el archivo "images/users/user:12345/profilePhoto.png" solo está sujeto al resultado de other_condition . .

Se puede hacer referencia a una variable comodín desde dentro de la autorización de ruta o nombre de archivo proporcionado por la match :

// Another way to restrict the name of a file
match /images/{imageId} {
  allow read: if imageId == "profilePhoto.png";
}

Las reglas de seguridad de Cloud Storage no se aplican en cascada y las reglas solo se evalúan cuando la ruta de la solicitud coincide con una ruta con las reglas especificadas.

Solicitar evaluación

Las cargas, descargas, cambios de metadatos y eliminaciones se evalúan mediante la request enviada a Cloud Storage. La variable de request contiene la ruta del archivo donde se realiza la solicitud, la hora en que se recibe la solicitud y el nuevo valor del resource si la solicitud es de escritura. También se incluyen los encabezados HTTP y el estado de autenticación.

El objeto de request también contiene la identificación única del usuario y la carga útil de autenticación de Firebase en el objeto request.auth , que se explicará con más detalle en la sección Autenticación de los documentos.

Una lista completa de propiedades en el objeto de request está disponible a continuación:

Propiedad Escribe Descripción
auth mapa<cadena, cadena> Cuando un usuario inicia sesión, proporciona uid , la identificación única del usuario y token , un mapa de reclamos de Firebase Authentication JWT. De lo contrario, será null .
params mapa<cadena, cadena> Mapa que contiene los parámetros de consulta de la solicitud.
path sendero Una path representa la ruta en la que se está realizando la solicitud.
resource mapa<cadena, cadena> El nuevo valor del recurso, presente solo en las solicitudes de write .
time marca de tiempo Una marca de tiempo que representa la hora del servidor en la que se evalúa la solicitud.

Evaluación de recursos

Al evaluar las reglas, es posible que también desee evaluar los metadatos del archivo que se está cargando, descargando, modificando o eliminando. Esto le permite crear reglas complejas y poderosas que hacen cosas como permitir que solo se carguen archivos con ciertos tipos de contenido, o que solo se eliminen archivos que superen cierto tamaño.

Firebase Security Rules for Cloud Storage proporciona metadatos de archivo en el objeto de resource , que contiene pares clave/valor de los metadatos que aparecen en un objeto de Cloud Storage. Estas propiedades se pueden inspeccionar en solicitudes de read o write para garantizar la integridad de los datos.

En solicitudes de write (como cargas, actualizaciones de metadatos y eliminaciones), además del objeto de resource , que contiene metadatos de archivo para el archivo que existe actualmente en la ruta de la solicitud, también tiene la capacidad de usar el objeto request.resource , que contiene un subconjunto de los metadatos del archivo que se escribirán si se permite la escritura. Puede usar estos dos valores para garantizar la integridad de los datos o hacer cumplir las restricciones de la aplicación, como el tipo o el tamaño del archivo.

Una lista completa de propiedades en el objeto de resource está disponible a continuación:

Propiedad Escribe Descripción
name cuerda El nombre completo del objeto.
bucket cuerda El nombre del depósito en el que reside este objeto.
generation En t La generación de objetos de Google Cloud Storage de este objeto.
metageneration En t La metageneración del objeto Google Cloud Storage de este objeto.
size En t El tamaño del objeto en bytes.
timeCreated marca de tiempo Una marca de tiempo que representa la hora en que se creó un objeto.
updated marca de tiempo Una marca de tiempo que representa la hora en que se actualizó por última vez un objeto.
md5Hash cuerda Un hash MD5 del objeto.
crc32c cuerda Un hash crc32c del objeto.
etag cuerda El etag asociado con este objeto.
contentDisposition cuerda La disposición de contenido asociada a este objeto.
contentEncoding cuerda La codificación de contenido asociada con este objeto.
contentLanguage cuerda El idioma del contenido asociado con este objeto.
contentType cuerda El tipo de contenido asociado con este objeto.
metadata mapa<cadena, cadena> Pares clave/valor de metadatos personalizados adicionales especificados por el desarrollador.

request.resource contiene todos estos a excepción de generation , metageneration , etag , timeCreated y updated .

Límites de las reglas de seguridad

Cuando trabaje con reglas de seguridad, tenga en cuenta los siguientes límites:

Límite Detalles
Número máximo de llamadas firestore.exists() y firestore.get() por solicitud

2 para solicitudes de un solo documento y solicitudes de consulta.

Superar este límite da como resultado un error de permiso denegado.

Las llamadas de acceso a los mismos documentos pueden almacenarse en caché y las llamadas en caché no cuentan para los límites.

Ejemplo completo

Poniéndolo todo junto, puede crear un ejemplo completo de reglas para una solución de almacenamiento de imágenes:

service firebase.storage {
 match /b/{bucket}/o {
   match /images {
     // Cascade read to any image type at any path
     match /{allImages=**} {
       allow read;
     }

     // Allow write files to the path "images/*", subject to the constraints:
     // 1) File is less than 5MB
     // 2) Content type is an image
     // 3) Uploaded content type matches existing content type
     // 4) File name (stored in imageId wildcard variable) is less than 32 characters
     match /{imageId} {
       allow write: if request.resource.size < 5 * 1024 * 1024
                    && request.resource.contentType.matches('image/.*')
                    && request.resource.contentType == resource.contentType
                    && imageId.size() < 32
     }
   }
 }
}

Base de datos en tiempo real

Estructura basica

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

Utilizan 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 refleja la estructura JSON de su base de datos.
  • Solicitud: estos son los métodos que usa la regla para otorgar acceso. Las reglas de read y write otorgan un amplio acceso de lectura y escritura, mientras que las reglas de validate actúan como una verificación secundaria para otorgar acceso en función de los datos entrantes o existentes.
  • Condición: la condición que permite una solicitud si se evalúa como verdadera.

Cómo se aplican las reglas a las rutas

En Realtime Database, las reglas se aplican de forma atómica, lo que significa que las reglas en los nodos principales de nivel superior anulan las reglas en los nodos secundarios más granulares y las reglas en un nodo más profundo no pueden otorgar acceso a una ruta principal. No puede refinar o revocar el acceso a una ruta más profunda en la estructura de su base de datos si ya lo ha otorgado para una de las rutas principales.

Considere las siguientes reglas:

{
  "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 baz secundario con valor true . La regla ".read": false en /foo/bar/ no tiene efecto aquí, ya que una ruta secundaria no puede revocar el acceso.

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 esfuerzo mínimo. Esto es particularmente útil para la seguridad basada en el usuario .

Sin embargo, las reglas de .validate no se aplican en cascada. Todas las reglas de validación deben cumplirse en todos los niveles de la jerarquía para que se permita una escritura.

Además, debido a que las reglas no se aplican de nuevo a una ruta principal, la operación de lectura o escritura falla si no hay una regla en la ubicación solicitada o en una ubicación principal que otorga 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 comprender que las reglas se evalúan atómicamente, podría parecer que obtener la ruta /records/ 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
Nota: Este producto de Firebase no está disponible en el objetivo App Clip.
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
Nota: Este producto de Firebase no está disponible en el objetivo App Clip.
var ref = FIRDatabase.database().reference()
ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // success block is not called
}, withCancelBlock: { error in
    // cancel block triggered with PERMISSION_DENIED
})
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // success method is not called
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback triggered with PERMISSION_DENIED
  });
});
DESCANSAR
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Dado que la operación de lectura en /records/ es atómica, y no hay una regla de lectura que otorgue acceso a todos los datos en /records/ , generará un error PERMISSION_DENIED . Si evaluamos esta regla en el simulador de seguridad en nuestra consola Firebase , podemos ver que la operación de lectura fue denegada:

Attempt to read /records with auth=Success(null)
    /
    /records

No .read rule allowed the operation.
Read was denied.

La operación fue denegada porque ninguna regla de lectura permitía el acceso a la ruta /records/ , pero 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
Nota: Este producto de Firebase no está disponible en el objetivo App Clip.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Rápido
Nota: Este producto de Firebase no está disponible en el objetivo App Clip.
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records/rec1");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // SUCCESS!
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback is not called
  }
});
DESCANSAR
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Variable de ubicación

Las reglas de la base de datos en tiempo real admiten una variable de $location para hacer coincidir los segmentos de la ruta. Use el prefijo $ delante de su segmento de ruta para hacer coincidir su regla con cualquier nodo secundario a lo largo de 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 puede usar la $variable en paralelo con nombres de rutas 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 }
      }
    }
  }