Conditions d'utilisation dans les règles de sécurité de Firebase Cloud Storage

Ce guide s'appuie sur l' apprentissage de la syntaxe de base du guide linguistique des règles de sécurité Firebase pour montrer comment ajouter des conditions à vos règles de sécurité Firebase pour le stockage dans le cloud.

Le principal élément constitutif des règles de sécurité du stockage cloud est la condition . Une condition est une expression booléenne qui détermine si une opération particulière doit être autorisée ou refusée. Pour les règles de base, utiliser des littéraux true et false comme conditions fonctionne parfaitement. Mais le langage Firebase Security Rules for Cloud Storage vous permet d'écrire des conditions plus complexes qui peuvent :

  • Vérifier l'authentification de l'utilisateur
  • Valider les données entrantes

Authentification

Les règles de sécurité Firebase pour Cloud Storage s'intègrent à Firebase Authentication pour fournir une puissante authentification basée sur l'utilisateur sur Cloud Storage. Cela permet un contrôle d'accès granulaire basé sur les revendications d'un jeton d'authentification Firebase.

Lorsqu'un utilisateur authentifié effectue une requête sur Cloud Storage, la variable request.auth est renseignée avec l' uid de l'utilisateur ( request.auth.uid ) ainsi que les revendications du JWT d'authentification Firebase ( request.auth.token ).

De plus, lors de l'utilisation de l'authentification personnalisée, des revendications supplémentaires apparaissent dans le champ request.auth.token .

Lorsqu'un utilisateur non authentifié effectue une requête, la variable request.auth est null .

À l'aide de ces données, il existe plusieurs manières courantes d'utiliser l'authentification pour sécuriser les fichiers :

  • Public : ignorer request.auth
  • Privé authentifié : vérifiez que request.auth n'est pas null
  • Utilisateur privé : vérifiez que request.auth.uid est égal à un uid de chemin
  • Groupe privé : vérifiez les revendications du jeton personnalisé pour qu'elles correspondent à une revendication choisie, ou lisez les métadonnées du fichier pour voir si un champ de métadonnées existe

Publique

Toute règle qui ne prend pas en compte le contexte request.auth peut être considérée comme une règle public , car elle ne prend pas en compte le contexte d'authentification de l'utilisateur. Ces règles peuvent être utiles pour faire apparaître des données publiques telles que des ressources de jeu, des fichiers audio ou tout autre contenu statique.

// 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");
}

Privé authentifié

Dans certains cas, vous souhaiterez peut-être que les données soient visibles par tous les utilisateurs authentifiés de votre application, mais pas par les utilisateurs non authentifiés. Puisque la variable request.auth est null pour tous les utilisateurs non authentifiés, il suffit de vérifier que la variable request.auth existe afin d'exiger une authentification :

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

Utilisateur privé

Le cas d'utilisation de loin le plus courant de request.auth sera de fournir aux utilisateurs individuels des autorisations granulaires sur leurs fichiers : du téléchargement de photos de profil à la lecture de documents privés.

Étant donné que les fichiers dans Cloud Storage ont un « chemin » complet vers le fichier, tout ce qu'il faut pour qu'un fichier soit contrôlé par un utilisateur est une information unique d'identification de l'utilisateur dans le préfixe du nom de fichier (comme l' uid de l'utilisateur) qui peut être vérifiée. lorsque la règle est évaluée :

// 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;
}

Groupe privé

Un autre cas d'utilisation tout aussi courant consistera à autoriser des autorisations de groupe sur un objet, par exemple en permettant à plusieurs membres d'une équipe de collaborer sur un document partagé. Il existe plusieurs approches pour y parvenir :

  • Créez un jeton personnalisé d'authentification Firebase qui contient des informations supplémentaires sur un membre du groupe (telles qu'un identifiant de groupe)
  • Inclure des informations de groupe (telles qu'un ID de groupe ou une liste d' uid autorisés) dans les métadonnées du fichier

Une fois ces données stockées dans les métadonnées du jeton ou du fichier, elles peuvent être référencées à partir d'une règle :

// 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;
}

Demander une évaluation

Les chargements, les téléchargements, les modifications de métadonnées et les suppressions sont évalués à l'aide de la request envoyée à Cloud Storage. En plus de l'ID unique de l'utilisateur et de la charge utile d'authentification Firebase dans l'objet request.auth comme décrit ci-dessus, la variable request contient le chemin du fichier où la requête est exécutée, l'heure à laquelle la requête est reçue et la nouvelle valeur resource si la demande est une écriture.

L'objet request contient également l'ID unique de l'utilisateur et la charge utile d'authentification Firebase dans l'objet request.auth , qui sera expliqué plus en détail dans la section Sécurité basée sur l'utilisateur de la documentation.

Une liste complète des propriétés de l'objet request est disponible ci-dessous :

Propriété Taper Description
auth carte<string, string> Lorsqu'un utilisateur est connecté, fournit uid , l'ID unique de l'utilisateur et token , une carte des revendications JWT d'authentification Firebase. Sinon, ce sera null .
params carte<string, string> Carte contenant les paramètres de requête de la requête.
path chemin Un path représentant le chemin sur lequel la demande est exécutée.
resource carte<string, string> La nouvelle valeur de ressource, présente uniquement sur les demandes write .
time horodatage Un horodatage représentant l'heure du serveur à laquelle la demande est évaluée.

Évaluation des ressources

Lors de l'évaluation des règles, vous souhaiterez peut-être également évaluer les métadonnées du fichier en cours de chargement, de téléchargement, de modification ou de suppression. Cela vous permet de créer des règles complexes et puissantes qui permettent par exemple d'autoriser uniquement le téléchargement de fichiers avec certains types de contenu ou la suppression de uniquement des fichiers d'une taille supérieure à une certaine taille.

Les règles de sécurité Firebase pour Cloud Storage fournissent des métadonnées de fichier dans l'objet resource , qui contient des paires clé/valeur des métadonnées apparues dans un objet Cloud Storage. Ces propriétés peuvent être inspectées lors de demandes read ou write pour garantir l'intégrité des données.

Sur les demandes write (telles que les téléchargements, les mises à jour de métadonnées et les suppressions), en plus de l'objet resource , qui contient les métadonnées du fichier qui existe actuellement au niveau du chemin de la demande, vous avez également la possibilité d'utiliser l'objet request.resource , qui contient un sous-ensemble des métadonnées du fichier à écrire si l'écriture est autorisée. Vous pouvez utiliser ces deux valeurs pour garantir l'intégrité des données ou appliquer des contraintes d'application telles que le type ou la taille du fichier.

Une liste complète des propriétés de l'objet resource est disponible ci-dessous :

Propriété Taper Description
name chaîne Le nom complet de l'objet
bucket chaîne Le nom du bucket dans lequel réside cet objet.
generation int Génération d'objet Google Cloud Storage de cet objet.
metageneration int Métagénération d'objet Google Cloud Storage de cet objet.
size int La taille de l'objet en octets.
timeCreated horodatage Un horodatage représentant l’heure à laquelle un objet a été créé.
updated horodatage Un horodatage représentant l’heure à laquelle un objet a été mis à jour pour la dernière fois.
md5Hash chaîne Un hachage MD5 de l'objet.
crc32c chaîne Un hachage crc32c de l'objet.
etag chaîne L'étiquette associée à cet objet.
contentDisposition chaîne Disposition du contenu associée à cet objet.
contentEncoding chaîne L'encodage du contenu associé à cet objet.
contentLanguage chaîne La langue du contenu associée à cet objet.
contentType chaîne Le type de contenu associé à cet objet.
metadata carte<string, string> Paires clé/valeur de métadonnées personnalisées supplémentaires spécifiées par le développeur.

request.resource contient tous ces éléments à l'exception de generation , metageneration , etag , timeCreated et updated .

Améliorez avec Cloud Firestore

Vous pouvez accéder aux documents dans Cloud Firestore pour évaluer d'autres critères d'autorisation.

À l'aide des fonctions firestore.get() et firestore.exists() , vos règles de sécurité peuvent évaluer les requêtes entrantes par rapport aux documents dans Cloud Firestore. Les fonctions firestore.get() et firestore.exists() attendent toutes deux des chemins de documents entièrement spécifiés. Lorsque vous utilisez des variables pour construire des chemins pour firestore.get() et firestore.exists() , vous devez échapper explicitement aux variables en utilisant la syntaxe $(variable) .

Dans l'exemple ci-dessous, nous voyons une règle qui restreint l'accès en lecture aux fichiers aux utilisateurs membres de clubs particuliers.

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
    }
  }
}
Dans l'exemple suivant, seuls les amis d'un utilisateur peuvent voir ses photos.
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))
    }
  }
}

Une fois que vous avez créé et enregistré vos premières règles de sécurité Cloud Storage qui utilisent ces fonctions Cloud Firestore, vous serez invité dans la console Firebase ou Firebase CLI pour activer les autorisations permettant de connecter les deux produits.

Vous pouvez désactiver la fonctionnalité en supprimant un rôle IAM, comme décrit dans Gérer et déployer les règles de sécurité Firebase .

Valider les données

Les règles de sécurité Firebase pour Cloud Storage peuvent également être utilisées pour la validation des données, notamment la validation du nom et du chemin du fichier ainsi que des propriétés des métadonnées du fichier telles que contentType et 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/.*');
    }
  }
}

Fonctions personnalisées

À mesure que vos règles de sécurité Firebase deviennent plus complexes, vous souhaiterez peut-être regrouper des ensembles de conditions dans des fonctions que vous pouvez réutiliser dans votre ensemble de règles. Les règles de sécurité prennent en charge les fonctions personnalisées. La syntaxe des fonctions personnalisées ressemble un peu à JavaScript, mais les fonctions des règles de sécurité Firebase sont écrites dans un langage spécifique au domaine qui présente certaines limitations importantes :

  • Les fonctions ne peuvent contenir qu'une seule instruction return . Ils ne peuvent contenir aucune logique supplémentaire. Par exemple, ils ne peuvent pas exécuter de boucles ni appeler des services externes.
  • Les fonctions peuvent accéder automatiquement aux fonctions et aux variables à partir de la portée dans laquelle elles sont définies. Par exemple, une fonction définie dans la portée service firebase.storage a accès à la variable resource et, pour Cloud Firestore uniquement, aux fonctions intégrées telles que get() et exists() .
  • Les fonctions peuvent appeler d'autres fonctions mais ne peuvent pas récidiver. La profondeur totale de la pile d’appels est limitée à 10.
  • Dans la version rules2 , les fonctions peuvent définir des variables à l'aide du mot-clé let . Les fonctions peuvent avoir n'importe quel nombre de liaisons let, mais doivent se terminer par une instruction return.

Une fonction est définie avec le mot-clé function et prend zéro ou plusieurs arguments. Par exemple, vous souhaiterez peut-être combiner les deux types de conditions utilisés dans les exemples ci-dessus en une seule fonction :

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();
    }
  }
}

L'utilisation de fonctions dans vos règles de sécurité Firebase les rend plus faciles à maintenir à mesure que la complexité de vos règles augmente.

Prochaines étapes

Après cette discussion sur les conditions, vous avez une compréhension plus sophistiquée des règles et êtes prêt à :

Apprenez à gérer les cas d'utilisation principaux et découvrez le flux de travail pour développer, tester et déployer des règles :