API des règles de sécurité de la base de données Firebase

Règle : Types

.lire

Accorde à un client un accès en lecture à un emplacement de base de données en temps réel Firebase.

Une règle .read est un type de règle de sécurité qui accorde à un client un accès en lecture à un emplacement de la base de données en temps réel Firebase. Par exemple:

 ".read": "auth != null && auth.provider == 'twitter'"

La valeur d'une règle .read est une chaîne, qui est évaluée comme un sous-ensemble de la syntaxe d'expression de JavaScript avec quelques changements de comportement pour augmenter la clarté et l'exactitude. Une règle .read qui accorde l'autorisation de lire un emplacement permettra également la lecture de tous les descendants de cet emplacement, même si les descendants ont leurs propres règles .read qui échouent.

Une règle .read a accès à toutes les variables de règle de la base de données en temps réel Firebase, à l'exception newData .

.écrire

Accorde à un client un accès en écriture à un emplacement de base de données en temps réel Firebase.

Une règle .write est un type de règle de sécurité qui accorde à un client un accès en écriture à un emplacement de base de données en temps réel Firebase. Par exemple:

".write": "auth != null && auth.token.isAdmin == true"

La valeur d'une règle .write est une chaîne, qui est évaluée comme un sous-ensemble de la syntaxe d'expression JavaScript avec quelques changements de comportement pour augmenter la clarté et l'exactitude. Une règle .write qui accorde l'autorisation d'écrire dans un emplacement permettra également d'écrire sur tous les descendants de cet emplacement, même si les descendants ont leurs propres règles .write qui échouent.

Une règle .write a accès à toutes les variables de règle de la base de données en temps réel Firebase.

.valider

Utilisé une fois qu'une règle .write a accordé l'accès, pour garantir que les données en cours d'écriture sont conformes à un schéma spécifique.

Une règle .validate est utilisée une fois qu'une règle .write a accordé l'accès, pour garantir que les données en cours d'écriture sont conformes à une norme spécifique. En plus d'un accès .write accordant, toutes les règles .validate pertinentes doivent réussir avant qu'une écriture ne soit autorisée. Par exemple:

".validate": "newData.hasChildren(['name', 'age'])"

La valeur d'une règle .validate est une chaîne, qui est évaluée comme un sous-ensemble de la syntaxe d'expression de JavaScript avec quelques changements de comportement pour augmenter la clarté et l'exactitude.

Une règle .validate a accès à toutes les variables de règle de la base de données en temps réel Firebase.

.indexOn

Améliore les performances des requêtes en indiquant à la base de données Firebase Realtime les clés sur lesquelles vous souhaitez que vos données soient indexées.

La règle .indexOn indique aux serveurs de base de données Firebase Realtime d'indexer des clés spécifiques dans vos données pour améliorer les performances de vos requêtes. Par exemple, étant donné une base de données contenant une collection de données sur les dinosaures, nous pouvons demander à Firebase Realtime Database d'optimiser les requêtes, avant qu'elles ne soient renvoyées par les serveurs, en ajoutant cette règle :

{
  "rules": {
    "dinosaurs": {
      ".indexOn": ["height", "length"]
    }
  }
}

Vous pouvez trouver plus d'informations sur la règle .indexOn en vous référant à la section du guide de sécurité sur l'indexation de vos données .

Règle : Variables

authentification

Une variable contenant la charge utile du jeton si un client est authentifié, ou null si le client n'est pas authentifié.

Firebase Realtime Database vous permet de vous authentifier facilement auprès de plusieurs fournisseurs intégrés et générera des jetons d'authentification pour eux. Une fois qu'un utilisateur est authentifié auprès de l'un des fournisseurs intégrés, la variable auth contiendra les éléments suivants :

Champ Description
provider La méthode d'authentification utilisée (par exemple "mot de passe", "anonyme", "facebook", "github", "google" ou "twitter").
uid Un identifiant utilisateur unique, garanti unique pour tous les fournisseurs.
token Le contenu du jeton d'identification d'authentification Firebase. Voir auth.token .

À titre d'exemple, nous pourrions avoir une règle comme celle-ci pour permettre aux utilisateurs de créer des commentaires à condition qu'ils stockent leur identifiant utilisateur avec le commentaire :

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && newData.child('user_id').val() == auth.uid"
    }
  }
}

Nous pourrions également établir une règle comme celle-ci pour permettre aux utilisateurs de créer des commentaires tant qu'ils sont connectés via Facebook :

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && auth.provider == 'facebook'"
    }
  }
}

jeton d'authentification

Une variable contenant le contenu du jeton d'identification d'authentification Firebase.

Le jeton contient tout ou partie des clés suivantes :

Champ Description
email L'adresse e-mail associée au compte, si présente.
email_verified true si l'utilisateur a vérifié qu'il a accès à l'adresse email . Certains fournisseurs vérifient automatiquement les adresses e-mail dont ils sont propriétaires.
phone_number Le numéro de téléphone associé au compte, s'il est présent.
name Le nom d’affichage de l’utilisateur, s’il est défini.
sub L'UID Firebase de l'utilisateur. C’est unique dans un projet.
firebase.identities Dictionnaire de toutes les identités associées au compte de cet utilisateur. Les clés du dictionnaire peuvent être les suivantes : email , phone , google.com , facebook.com , github.com , twitter.com . Les valeurs du dictionnaire sont des tableaux d'identifiants uniques pour chaque fournisseur d'identité associé au compte. Par exemple, auth.token.firebase.identities["google.com"][0] contient le premier identifiant Google associé au compte.
firebase.sign_in_provider Fournisseur de connexion utilisé pour obtenir ce jeton. Il peut s'agir de l'une des chaînes suivantes : custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com .
firebase.tenant Le tenantId associé au compte, s'il est présent. par exemple tenant2-m6tyz

Si vous utilisez une authentification personnalisée, auth.token contient également toutes les revendications personnalisées spécifiées par le développeur.

Toutes ces valeurs peuvent être utilisées dans les règles. Par exemple, pour restreindre l'accès aux comptes Google associés à une adresse gmail.com, on pourrait ajouter la règle :

{
  "rules": {
    ".read": "auth != null",
    "gmailUsers": {
      "$uid": {
        ".write": "auth.token.email_verified == true && auth.token.email.matches(/.*@gmail.com$/)"
      }
    }
  }
}

Par souci d'exhaustivité, les champs suivants sont également inclus dans auth.token , mais il est peu probable qu'ils soient utiles pour les règles.

Champ Description
iss L'émetteur du jeton.
aud Le public du jeton.
auth_time La dernière fois que l'utilisateur s'est authentifié avec un identifiant à l'aide de l'appareil recevant le jeton.
iat L'heure à laquelle le jeton a été émis.
exp L'heure à laquelle le jeton expire.

$emplacement

Une variable qui peut être utilisée pour référencer la clé d'un $location qui a été utilisée précédemment dans une structure de règles.

Lorsque vous avez un $location dans votre structure de règles, vous pouvez utiliser une variable $ correspondante dans votre expression de règle pour obtenir le nom de l'enfant réel en cours de lecture ou d'écriture. Supposons donc que nous voulions donner à chaque utilisateur un accès en lecture et en écriture à son propre emplacement /users/<user> . Nous pourrions utiliser :

{
  "rules": {
    "users": {
      "$user": {
        ".read": "auth.uid === $user",
        ".write": "auth.uid === $user"
      }
    }
  }
}

Lorsqu'un client tente d'accéder à /users/barney , l'emplacement par défaut $user correspondra à $user étant égal à "barney". Ainsi, la règle .read vérifiera si auth.uid === 'barney' . Par conséquent, la lecture de /users/barney ne réussira que si le client est authentifié avec un uid « barney ».

maintenant

Contient le nombre de millisecondes depuis l'époque Unix selon les serveurs Firebase Realtime Database.

La variable now contient le nombre de millisecondes depuis l'époque UNIX selon les serveurs Firebase Realtime Database. Par exemple, vous pouvez utiliser ceci pour vérifier que l'heure created d'un utilisateur n'est jamais définie sur une heure future :

{
  "rules": {
    "users": {
      "$user": {
        "created": {
          ".validate": "newData.val() < now"
        }
      }
    }
  }
}

racine

Un RuleDataSnapshot correspondant aux données actuelles à la racine de votre base de données Firebase Realtime.

La variable racine vous donne un RuleDataSnapshot correspondant aux données actuelles à la racine de votre base de données Firebase Realtime. Vous pouvez l'utiliser pour lire toutes les données de votre base de données dans vos expressions de règle. Par exemple, si nous voulions permettre aux utilisateurs de lire /comments uniquement si leur /users/<id>/active était défini sur true, nous pourrions utiliser :

{
  "rules": {
    "comments": {
      ".read": "root.child('users').child(auth.uid).child('active').val() == true"
    }
  }
}

Ensuite, si /users/barney/active contenait la valeur true, un utilisateur authentifié avec un uid de « barney » pourrait écrire sur le nœud /comments .

données

Un RuleDataSnapshot correspondant aux données actuelles dans la base de données Firebase Realtime à l'emplacement de la règle en cours d'exécution.

La variable data vous donne un RuleDataSnapshot correspondant aux données actuelles dans l'emplacement de la base de données de la règle en cours d'exécution (par opposition à root, qui vous donne les données pour la racine de votre base de données).

Ainsi, par exemple, si vous souhaitez permettre à n'importe quel client d'accéder à /users/<user> si /users/<user>/public est défini sur true, vous pouvez utiliser :

{
  "rules": {
    "users": {
      "$user": {
        ".read": "data.child('public').val() == true"
      }
    }
  }
}

La variable data est disponible dans les règles .read , .write et .validate .

nouvelles données

Un RuleDataSnapshot correspondant aux données qui résulteront si l'écriture est autorisée.

Pour les règles .write et .validate , la variable newData vous donne un RuleDataSnapshot correspondant aux données qui résulteront si l'écriture est autorisée (il s'agit d'une "fusion" des données existantes plus les nouvelles données en cours d'écriture). Donc, si vous voulez vous assurer que chaque utilisateur a un nom et un âge, vous pouvez utiliser :

{
  "rules": {
    "users": {
      "$user": {
        ".read": true,
        ".write": true,
        ".validate": "newData.hasChildren(['name', 'age'])"
      }
    }
  }
}

Puisque newData fusionne les données existantes et les nouvelles données, il se comporte correctement même pour les mises à jour « partielles ». Par exemple:

var fredRef = firebase.database().ref("users/fred");
// Valid since we have a name and age.
fredRef.set({ name: "Fred", age: 19 });
// Valid since we are updating the name but there's already an age.
fredRef.child("age").set(27);
// Invalid since the .validate rule will no longer be true.
fredRef.child("name").remove();

La variable newData n'est pas disponible dans les règles .read car aucune nouvelle donnée n'est écrite. Vous devriez simplement utiliser data .

RuleDataSnapshot : méthodes

val()

Obtient la valeur primitive ( string , number , boolean ou null ) de ce RuleDataSnapshot .

Valeur de retour : ( String , Number , Boolean , Null ) - La valeur primitive de ce RuleDataSnapshot .

Contrairement à DataSnapshot.val() , l'appel val() sur un RuleDataSnapshot contenant des données enfants ne renverra pas d'objet contenant les enfants. Il renverra à la place une valeur sentinelle spéciale. Cela garantit que les règles peuvent toujours fonctionner de manière extrêmement efficace.

Par conséquent, vous devez toujours utiliser child() pour accéder aux enfants (par exemple data.child('name').val() , pas data.val().name ).

Cet exemple autorise la lecture uniquement si l'enfant isReadable est défini sur true à l'emplacement en cours de lecture.

".read": "data.child('isReadable').val() == true"

enfant()

Obtient un RuleDataSnapshot pour l'emplacement au chemin relatif spécifié.

Arguments : childPath String - Un chemin relatif vers l'emplacement des données enfants.

Valeur de retour : RuleDataSnapshot - Le RuleDataSnapshot pour l'emplacement enfant.

Le chemin relatif peut être soit un simple nom d'enfant (par exemple 'fred') ou un chemin plus profond séparé par des barres obliques (par exemple 'fred/nom/premier'). Si l'emplacement enfant ne contient aucune donnée, un RuleDataSnapshot vide est renvoyé.

Cet exemple autorise la lecture uniquement si l'enfant isReadable est défini sur true à l'emplacement en cours de lecture.

".read": "data.child('isReadable').val() == true"

parent()

Obtient un RuleDataSnapshot pour l'emplacement parent.

Valeur de retour : RuleDataSnapshot - Le RuleDataSnapshot pour l'emplacement parent.

Si cette instance fait référence à la racine de votre base de données Firebase Realtime, elle n'a pas de parent et parent() échouera, entraînant l'omission de l'expression de règle actuelle (comme un échec).

Cet exemple autorise la lecture uniquement si le frère isReadable est défini sur true.

".read": "data.parent().child('isReadable').val() == true"

hasChild (chemin enfant)

Renvoie vrai si l'enfant spécifié existe.

Arguments : childPath String - Un chemin relatif vers l'emplacement d'un enfant potentiel.

Valeur de retour : Booléen - true si les données existent sur le chemin enfant spécifié ; sinon false .

Cet exemple permet uniquement d'écrire les données si elles contiennent un "nom" enfant.

".validate": "newData.hasChild('name')"

aEnfants([enfants])

Vérifie l'existence des enfants.

Arguments : children Array facultatif - Un tableau de clés enfants qui doivent toutes exister.

Valeur de retour : Boolean - true si les enfants (spécifiés) existent ; sinon false .

Si aucun argument n'est fourni, il retournera vrai si RuleDataSnapshot a des enfants. Si un tableau de noms d'enfants est fourni, il renverra true uniquement si tous les enfants spécifiés existent dans RuleDataSnapshot .

Cet exemple permet uniquement d'écrire les données si elles contiennent un ou plusieurs enfants.

".validate": "newData.hasChildren()"

Cet exemple permet uniquement d'écrire les données si elles contiennent des enfants "nom" et "âge".

".validate": "newData.hasChildren(['name', 'age'])"

existe()

Renvoie vrai si ce RuleDataSnapshot contient des données.

Valeur de retour : Boolean - true si RuleDataSnapshot contient des données ; sinon false .

La fonction exist renvoie true si ce RuleDataSnapshot contient des données. Il s'agit simplement d'une fonction pratique puisque data.exists() est équivalent à data.val() != null .

Cet exemple autorise une écriture à cet emplacement tant qu'il n'y a pas de données existantes.

".write": "!data.exists()"

obtenirPriorité()

Obtient la priorité des données dans un RuleDataSnapshot .

Valeur de retour : ( String , Number , Null ) - La priorité des données dans ce RuleDataSnapshot .

Cet exemple garantit que les nouvelles données en cours d'écriture ont une priorité

".validate": "newData.getPriority() != null"

estNuméro()

Renvoie vrai si ce RuleDataSnapshot contient une valeur numérique.

Valeur de retour : Boolean - true si les données sont numériques ; sinon false .

Cet exemple garantit que les nouvelles données en cours d'écriture ont un "âge" enfant avec une valeur numérique.

".validate": "newData.child('age').isNumber()"

estString()

Renvoie vrai si ce RuleDataSnapshot contient une valeur de chaîne.

Valeur de retour : Boolean - true si les données sont une String ; sinon false .

Cet exemple garantit que les nouvelles données en cours d'écriture ont un « nom » enfant avec une valeur de chaîne.

".validate": "newData.child('name').isString()

estBooléen()

Renvoie vrai si ce RuleDataSnapshot contient une valeur booléenne.

Valeur de retour : Boolean - true si les données sont Boolean ; sinon false .

Cet exemple garantit que les nouvelles données en cours d'écriture ont un enfant « actif » avec une valeur booléenne.

".validate": "newData.child('active').isBoolean()"

Chaîne : Propriétés

longueur

Renvoie la longueur de la chaîne.

Valeur de retour : Number - Le nombre de caractères dans la chaîne.

Cet exemple nécessite que la chaîne comporte au moins 10 caractères.

".validate": "newData.isString() && newData.val().length >= 10"

Chaîne : méthodes

contient (sous-chaîne)

Renvoie vrai si la chaîne contient la sous-chaîne spécifiée.

Arguments : substring String - Une sous-chaîne à rechercher.

Valeur de retour : Boolean - true si la chaîne contient la sous-chaîne spécifiée ; sinon false .

Cet exemple nécessite que les données soient une chaîne contenant « @ ».

".validate": "newData.isString() && newData.val().contains('@')"

commenceAvec(sous-chaîne)

Renvoie vrai si la chaîne commence par la sous-chaîne spécifiée.

Arguments : substring String - Une sous-chaîne à rechercher au début.

Valeur de retour : Boolean - true si la chaîne contient la sous-chaîne spécifiée ; sinon false .

Cet exemple autorise l'accès en lecture si auth.token.identifier commence par "interne-"

".read": "auth.token.identifier.beginsWith('internal-')"

se termine par (sous-chaîne)

Renvoie vrai si la chaîne se termine par la sous-chaîne spécifiée.

Arguments : substring String - Une sous-chaîne à rechercher à la fin.

Valeur de retour : Boolean - true si la chaîne se termine par la sous-chaîne spécifiée ; sinon false .

Cet exemple autorise l'accès en lecture si auth.token.identifier se termine par "@company.com"

".read": "auth.token.identifier.endsWith('@company.com')"

remplacer (sous-chaîne, remplacement)

Renvoie une copie de la chaîne avec toutes les instances d'une sous-chaîne spécifiée remplacées par la chaîne de remplacement spécifiée.

Arguments : substring String - Une sous-chaîne à rechercher. replacement String - Une chaîne par laquelle remplacer la sous-chaîne.

Valeur de retour : String - La nouvelle chaîne après avoir remplacé la sous-chaîne par le remplacement.

La méthode replace() diffère légèrement de la méthode JavaScript replace() dans la mesure où elle remplace toutes les instances d'une sous-chaîne spécifiée par la chaîne de remplacement spécifiée, et pas seulement la première instance.

Étant donné que les points ne sont pas autorisés dans les clés, nous devons échapper les chaînes avec des points avant de les stocker. Un exemple de ceci serait avec les adresses e-mail. Supposons que nous ayons une liste d'adresses e-mail sur liste blanche dans notre nœud /whitelist/ :

{
 "user": {
   "$uid": {
     "email": <email>
   }
 },
 "whitelist": {
   "fred@gmail%2Ecom": true,
   "barney@aol%2Ecom": true
 }
}

Nous pouvons créer une règle qui autorise l'ajout d'utilisateurs uniquement si leur adresse e-mail se trouve dans le nœud /whitelist/ :

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "true",
        ".write": "root.child('whitelist').child(newData.child('email').val().replace('.', '%2E')).exists()"
      }
    }
  }
}

versMinuscule()

Renvoie une copie de la chaîne convertie en minuscules.

Valeur de retour : String - La chaîne convertie en minuscules.

Cet exemple permet l'accès en lecture si auth.token.identifier car toutes les minuscules existent sous /users .

".read": "root.child('users').child(auth.token.identifier.toLowerCase()).exists()"

versUpperCase()

Renvoie une copie de la chaîne convertie en majuscules.

Valeur de retour : String - La chaîne convertie en majuscules.

Cet exemple permet l'accès en lecture si auth.token.identifier car toutes les majuscules existent sous /users .

".read": "root.child('users').child(auth.token.identifier.toUpperCase()).exists()"

correspondances (regex)

Renvoie vrai si la chaîne correspond au littéral d’expression régulière spécifié.

Valeur de retour : Boolean - true si la chaîne correspond au littéral de l'expression régulière, regex ; sinon false .

Voir la documentation complète des règles regex .

Les opérateurs

+ (ajouter)

Utilisé pour ajouter des variables ou pour la concaténation de chaînes.

L'exemple suivant garantit que la nouvelle valeur incrémente la valeur existante d'exactement une. Ceci est utile pour implémenter un compteur :

".write": "newData.val() === data.val() + 1"
".validate": "root.child('room_names/' + $room_id).exists()"

- (annuler ou soustraire)

Utilisé pour annuler une valeur ou soustraire deux valeurs dans une expression de règles.

Cette règle de validation vérifie que la nouvelle valeur est l'inverse d'une valeur enfant à l'emplacement :

".validate": "newData.val() === -(data.child('quantity').val())"

L'exemple suivant utilise la soustraction pour garantir que seuls les messages des dix dernières minutes peuvent être lus :

".read": "newData.child('timestamp').val() > (now - 600000)"

* (multiplier)

Utilisé pour multiplier des variables dans une expression de règles.

Cette règle de validation vérifie si la nouvelle valeur est égale au produit en prix et en quantité (deux valeurs existantes) :

".validate": "newData.val() === data.child('price').val() * data.child('quantity').val()"

/ (diviser)

Utilisé pour diviser des variables dans une expression de règles.

Dans l'exemple suivant, la règle de validation garantit que les données stockées correspondent à la moyenne du total des données stockées ailleurs :

".validate": "newData.val() === data.parent().child('sum').val() / data.parent().child('numItems').val()"

% (module)

Utilisé pour trouver le reste de la division d'une variable par une autre dans une expression de règles.

Cette règle valide que seuls les nombres pairs peuvent être écrits :

".validate": "newData.val() % 2 === 0"

=== (égal)

Utilisé pour vérifier si deux variables dans une expression de règles ont le même type et la même valeur.

La règle suivante utilise l'opérateur === pour accorder l'accès en écriture uniquement au propriétaire du compte utilisateur. L'uid de l'utilisateur doit correspondre exactement à la clé ( $user_id ) pour que la règle soit évaluée comme vraie.

"users": {
  ".write": "$user_id === auth.uid"
}

!== (pas égal)

Utilisé pour vérifier si deux variables dans une expression de règles ne sont pas égales.

La règle de lecture suivante garantit que seuls les utilisateurs connectés peuvent lire les données :

".read": "auth !== null"

&& (ET)

Prend la valeur true si les deux opérandes sont vrais. Utilisé pour évaluer plusieurs conditions dans une expression de règles.

La règle de validation suivante vérifie que les nouvelles données sont une chaîne de moins de 100 caractères :

".validate": "newData.isString() && newData.val().length < 100"

|| (OU)

Prend la valeur true si un opérande de l'expression de règles est vrai.

Dans cet exemple, nous pouvons écrire tant qu’il n’existe pas d’anciennes données ou de nouvelles données. En d’autres termes, nous pouvons écrire si nous supprimons ou créons des données, mais pas si nous mettons à jour des données.

".write": "!data.exists() || !newData.exists()"

! (PAS)

Prend la valeur true si son seul opérande est faux. Dans les expressions de règles, le ! L'opérateur est souvent utilisé pour voir si des données ont été écrites à un emplacement.

La règle suivante autorise l'accès en écriture uniquement s'il n'y a aucune donnée à l'emplacement spécifié :

".write": "!data.exists()"

> (supérieur à)

Utilisé pour vérifier si une valeur est supérieure à une autre valeur dans une expression de règles.

Cette règle de validation vérifie que la chaîne en cours d'écriture n'est pas une chaîne vide :

".validate": "newData.isString() && newData.val().length > 0"

< (Inférieur à)

Utilisé pour vérifier si une valeur est inférieure à une autre valeur dans une expression de règles.

Cette règle de validation vérifie qu'une chaîne comporte moins de 20 caractères :

".validate": "newData.isString() && newData.val().length < 20"

>= (supérieur ou égal à)

Utilisé pour vérifier si une valeur est supérieure ou égale à une autre valeur dans une expression de règles.

Cette règle de validation vérifie que la chaîne en cours d'écriture n'est pas une chaîne vide :

".validate": "newData.isString() && newData.val().length >= 1"

<= (inférieur ou égal à)

Utilisé pour vérifier si une valeur est inférieure ou égale à une autre valeur dans une expression de règles.

Cette règle de validation garantit que de nouvelles données ne pourront pas être ajoutées à l'avenir :

".validate": "newData.val() <= now"

? (opérateur ternaire)

Utilisé pour évaluer une expression de règles conditionnelles.

L'opérateur ternaire prend trois opérandes. L'opérande avant le ? est la condition. Si la condition est vraie, le deuxième opérande est évalué. Si la condition est fausse, le troisième opérande est évalué.

Pour la règle de validation suivante, la nouvelle valeur peut être un nombre ou un booléen. Si c'est un nombre, il doit être supérieur à 0.

".validate": "newData.isNumber() ? newData.val() > 0 : newData.isBoolean()"