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 reglas de seguridad de Firebase. Aquí aprenderás a agregar condiciones a tus reglas de seguridad de Firebase de Cloud Storage.

El elemento principal de las reglas de seguridad de Cloud Storage 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, las reglas de seguridad de Firebase para el lenguaje de Cloud Storage te brindan diferentes maneras de escribir condiciones más complejas que pueden realizarse en los siguientes casos:

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

Authentication

Las reglas de seguridad de Firebase para Cloud Storage se integran a Firebase Authentication para brindarte autenticación sólida basada en el usuario en Cloud Storage, lo cual permite controlar el acceso de forma detallada en función de las reclamaciones de un token de Firebase Authentication.

Cuando un usuario autenticado realiza una solicitud a Cloud Storage, la variable request.auth se propaga con el uid (request.auth.uid) del usuario, al igual que las reclamaciones de los JWT de 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 acerca de 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 el request enviado 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 JWT de Firebase Authentication. De lo contrario, será null.
params mapa <string, string> Un mapa que contiene los parámetros de consulta de la solicitud.
path ruta 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 marca de tiempo 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.

Las reglas de seguridad de Firebase para Cloud Storage proporcionan metadatos de archivo en el objeto 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 marca de tiempo Una marca de tiempo que representa la hora a la que se creó el objeto.
updated marca de tiempo 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.

Cómo agregar mejoras con Cloud Firestore

Puedes acceder a documentos en Cloud Firestore para evaluar otros criterios de autorización.

Con las funciones firestore.get() y firestore.exists(), las 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 primeras reglas de seguridad de Cloud Storage que usan estas funciones de Cloud Firestore, se te pedirá que habilites los permisos para conectar los dos productos en Firebase console o Firebase CLI.

Puedes inhabilitar la función si quitas un rol de IAM, como se describe en Implementa y administra las reglas de seguridad de Firebase.

Valida datos

Las reglas de seguridad de Firebase para Cloud Storage también se pueden utilizar para la validación de datos, lo cual incluye la validación del nombre de archivo y la ruta de acceso, así como las propiedades de los metadatos de 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 reglas de seguridad de Firebase 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 de Firebase 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 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 hacen 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 las reglas de seguridad de Firebase las hace más fáciles de mantener a medida que aumenta la complejidad de las reglas.

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