Usa condiciones en las reglas de seguridad de Firebase de Cloud Storage

Esta guía complementa el artículo sobre la sintaxis principal del lenguaje de Firebase Security Rules. Aquí aprenderás a agregar condiciones a tu Firebase Security Rules para Cloud Storage.

El componente básico principal de Cloud Storage Security Rules es la condición. Una condición es una expresión booleana que determina si se debe permitir o rechazar una operación en particular. Para reglas básicas, el uso de literales de true y false como condiciones funciona de manera correcta. Sin embargo, el lenguaje de Firebase Security Rules para Cloud Storagete brinda diferentes maneras de escribir condiciones más complejas que pueden realizar las siguientes acciones:

  • Verificar la autenticación de los usuarios
  • Validar los datos entrantes

Autenticación

Firebase Security Rules para Cloud Storage se integra con Firebase Authentication para proporcionar autenticación potente basada en usuarios para Cloud Storage. Esto permite un control detallado del acceso en función de las reclamaciones de un token Firebase Authentication.

Cuando un usuario autenticado realiza una solicitud a Cloud Storage, la variable request.auth se propaga con el uid del usuario (request.auth.uid), al igual que las reclamaciones de JWT Firebase Authentication (request.auth.token).

Además, cuando se utiliza la autenticación personalizada, las reclamaciones adicionales se muestran en el campo request.auth.token.

Cuando un usuario no autenticado realiza una solicitud, la variable request.auth es null.

Si usas estos datos, existen varias formas comunes en las que puedes usar la autenticación para proteger archivos:

  • Público: Ignora request.auth.
  • Privado autenticado: Comprueba que request.auth no sea null.
  • Usuario privado: Comprueba que request.auth.uid sea igual al uid de una ruta.
  • Grupo privado: Comprueba que las reclamaciones del token personalizado coincidan con una reclamación elegida o lee los metadatos de archivos para ver si existe un campo de metadatos.

Público

Cualquier regla que omita el contexto request.auth puede considerarse como una regla public, ya que no considera el contexto de autenticación del usuario. Estas reglas pueden ser útiles para mostrar datos públicos como elementos de juegos, archivos de sonido o contenido estático de otro tipo.

// Anyone to read a public image if the file is less than 100kB
// Anyone can upload a public file ending in '.txt'
match /public/{imageId} {
  allow read: if resource.size < 100 * 1024;
  allow write: if imageId.matches(".*\\.txt");
}

Privado autenticado

En ciertos casos, es necesario que todos los usuarios autenticados en tu aplicación puedan ver datos, pero no así los usuarios no autenticados. Dado que la variable request.auth es null para todos los usuarios no autenticados, lo único que debes hacer es comprobar que la variable request.auth existe, a fin de requerir la autenticación:

// Require authentication on all internal image reads
match /internal/{imageId} {
  allow read: if request.auth != null;
}

Usuario privado

Sin duda, el caso de uso más común de request.auth será otorgar permisos detallados a los archivos de cada usuario: desde la carga de fotos de perfil hasta la lectura de documentos privados.

Dado que los archivos almacenados en Cloud Storage tienen una “ruta de acceso” completa al archivo, todo lo que se requiere para que un usuario pueda controlar un archivo es información de identificación exclusiva que identifique al usuario en el prefijo del nombre del archivo (como el uid del usuario) que se puede verificar cuando se evalúe la regla:

// Only a user can upload their profile picture, but anyone can view it
match /users/{userId}/profilePicture.png {
  allow read;
  allow write: if request.auth.uid == userId;
}

Grupo privado

Otro caso de uso igualmente común será otorgar permisos de grupo en un objeto, como permitir que varios miembros de un equipo colaboren en un documento compartido. Existen varias maneras de hacer esto:

  • Crear un token personalizado de Firebase Authentication que contenga información adicional sobre un miembro del grupo (como el ID del grupo)
  • Incluir información del grupo (como su ID o una lista de uid autorizados) en los metadatos del archivo

Una vez que se almacenan esos datos en el token o en los metadatos de archivos, se puede hacer referencia a ellos desde una regla:

// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
  allow read: if resource.metadata.owner == request.auth.token.groupId;
  allow write: if request.auth.token.groupId == groupId;
}

Evaluación de solicitudes

Las cargas, las descargas, los cambios de metadatos y las eliminaciones se evalúan con la request enviada a Cloud Storage. Además del ID único del usuario y la carga útil de Firebase Authentication en el objeto request.auth, como se describió antes, la variable request contiene la ruta de acceso del archivo en la que se realiza la solicitud, el momento en que se recibe la solicitud y el nuevo valor de resource si la solicitud es una operación de escritura.

El objeto request también contiene el ID único del usuario y la carga útil de Firebase Authentication en el objeto request.auth, que se explicará con más detalle en la sección Seguridad basada en el usuario de la documentación.

A continuación, se muestra una lista completa de las propiedades del objeto request:

Propiedad Tipo Descripción
auth mapa <string, string> Cuando un usuario accede, proporciona el uid, el ID único del usuario y token, un mapa de las reclamaciones de Firebase Authentication JWT. De lo contrario, será null.
params mapa <string, string> Un mapa que contiene los parámetros de consulta de la solicitud.
path path Un objeto path que representa la ruta en la que se realiza la solicitud.
resource mapa <string, string> El nuevo valor del recurso, presente solo en solicitudes de tipo write.
time timestamp Una marca de tiempo que representa la hora del servidor a la que se evalúa la solicitud.

Evaluación de recursos

Para la evaluación de reglas, te recomendamos que también evalúes los metadatos del archivo que se va a subir, descargar, modificar o borrar. Esto te permite crear reglas complejas y potentes que ejecutan tareas como permitir que se suban archivos con ciertos tipos de contenido solamente o que solo se borren los archivos que superen cierto tamaño.

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

En las solicitudes write (como cargas, actualizaciones de metadatos y eliminaciones), además del objeto resource, que contiene metadatos de archivo para el archivo que existe actualmente en la ruta de la solicitud, también puedes usar el objeto request.resource, que contiene un subconjunto de los metadatos del archivo que se escribirán si se permite la escritura. Puedes 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 de los archivos.

A continuación, se muestra una lista completa de las propiedades del objeto resource:

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

request.resource contiene todas ellas, excepto generation, metageneration, etag, timeCreated y updated.

Mejora con Cloud Firestore

Puedes acceder a documentos en Cloud Firestore para evaluar otras autorizaciones con tus criterios.

Con las funciones firestore.get() y firestore.exists(), tus reglas de seguridad pueden evaluar las solicitudes entrantes en comparación con los documentos de Cloud Firestore. Las funciones firestore.get() y firestore.exists() usan rutas de acceso a documentos especificadas por completo. Cuando usas variables a fin de construir rutas de acceso para firestore.get() y firestore.exists(), debes escapar las variables de forma explícita con la sintaxis $(variable).

En el siguiente ejemplo, vemos una regla que restringe el acceso de lectura a los archivos para los usuarios que son miembros de clubes en particular.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.id)).memberships
    }
  }
}
En el siguiente ejemplo, solo los amigos de un usuario pueden ver sus fotos.
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id))
    }
  }
}

Una vez que crees y guardes tus primeros Cloud Storage Security Rules que usan estas funciones de Cloud Firestore se te solicitará en la consola de Firebase o en la CLI de Firebase que realices para habilitar los permisos para conectar los dos productos.

Puedes inhabilitar la función quitando un rol de IAM, como se describe en Implementa y administra Firebase Security Rules.

Valida datos

Firebase Security Rules para Cloud Storage también se puede usar para la validación de datos, lo que incluye validar el nombre y la ruta del archivo, así como las propiedades de los metadatos del archivo, como contentType y size.

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      // Only allow uploads of any image file that's less than 5MB
      allow write: if request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

Funciones personalizadas

A medida que tus Firebase Security Rules 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 Firebase Security Rules se escriben en un lenguaje específico del dominio, que 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 permiso service firebase.storage tiene acceso a la variable resource y, solo en Cloud Firestore, a 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 10.
  • En la versión rules2, las funciones pueden definir variables con la palabra clave let. Las funciones pueden tener cualquier cantidad de vinculaciones let, pero deben terminar con una declaración de retorno.

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 firebase.storage {
  match /b/{bucket}/o {
    // 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 /images/{imageId} {
      allow read, write: if signedInOrPublic();
    }
    match /mp3s/{mp3Ids} {
      allow read: if signedInOrPublic();
    }
  }
}

Usar funciones en tu Firebase Security Rules hace que sean más fáciles de mantener debido a su complejidad de tus reglas crece.

Próximos pasos

Después de leer este artículo sobre las condiciones, comprenderás las reglas en más profundidad y podrás hacer lo siguiente:

Aprender a abordar los casos de uso principales y conocer el flujo de trabajo para desarrollar, probar e implementar reglas