Catch up on everthing we announced at this year's Firebase Summit. Learn more

Fonctionnement des règles de sécurité

La sécurité peut être l'une des pièces les plus complexes du puzzle du développement d'applications. Dans la plupart des applications, les développeurs doivent créer et exécuter un serveur qui gère l'authentification (qui est un utilisateur) et l'autorisation (ce qu'un utilisateur peut faire).

Les règles de sécurité Firebase suppriment la couche intermédiaire (serveur) et vous permettent de spécifier des autorisations basées sur le chemin pour les clients qui se connectent directement à vos données. Utilisez ce guide pour en savoir plus sur la façon dont les règles sont appliquées aux demandes entrantes.

Sélectionnez un produit pour en savoir plus sur ses règles.

Cloud Firestore

Structure basique

Les règles de sécurité Firebase dans Cloud Firestore et Cloud Storage utilisent la structure et la syntaxe suivantes :

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

Les concepts clés suivants sont importants à comprendre lorsque vous créez les règles :

  • Demande: La méthode ou les méthodes invoquées dans la allow déclaration. Ce sont des méthodes que vous autorisez à exécuter. Les méthodes standard sont: get , list , create , update à delete update et delete . Les read et write méthodes pratiques permettent un large accès de lecture et d' écriture sur la base de données ou d'une trajectoire de stockage.
  • Path: L'emplacement de base de données ou de stockage, représentée comme un chemin d' accès d' URI.
  • Règle: La allow déclaration, qui comprend une condition qui permet une demande si elle est évaluée à true.

Règles de sécurité version 2

Depuis mai 2019, la version 2 des règles de sécurité Firebase est désormais disponible. La version 2 des règles modifie le comportement des jokers récursives {name=**} . Vous devez utiliser la version 2 si vous prévoyez d'utiliser les requêtes de groupe de collecte . Vous devez opt-in pour la version 2 en faisant rules_version = '2'; la première ligne de vos règles de sécurité :

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

Chemins correspondants

Toutes les instructions de correspondance doivent pointer vers des documents, pas vers des collections. Une déclaration de correspondance peut pointer vers un document spécifique, comme dans match /cities/SF ou utiliser des jokers pour pointer vers un document dans le chemin d' accès spécifié, comme dans match /cities/{city} .

Dans l'exemple ci - dessus, la déclaration de correspondance utilise la {city} syntaxe générique. Cela signifie que la règle s'applique à tout document dans la cities collection, telles que /cities/SF ou /cities/NYC . Lorsque le allow les expressions dans la déclaration de correspondance sont évalués, la city Résout à la ville le nom du document, comme SF ou NYC .

Sous-collections correspondantes

Les données dans Cloud Firestore sont organisées en collections de documents, et chaque document peut étendre la hiérarchie via des sous-collections. Il est important de comprendre comment les règles de sécurité interagissent avec les données hiérarchiques.

Considérez la situation où chaque document dans la cities collection contient un landmarks subcollection. Les règles de sécurité ne concernent que sur le chemin adapté, de sorte que les contrôles d'accès définis dans les cities collection ne sont pas applicables aux landmarks de landmarks subcollection. À la place, écrivez des règles explicites pour contrôler l'accès aux sous-collections :

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

Lors de l' imbrication match déclarations, le chemin du intérieur match de déclaration est toujours par rapport à la trajectoire de l'extérieur match de déclaration. Les ensembles de règles suivants sont donc équivalents :

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

Caractères génériques récursifs

Si vous voulez des règles à appliquer à une hiérarchie arbitraire profonde, utilisez la syntaxe générique récursif, {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>;
    }
  }
}

Lors de l'utilisation de la syntaxe générique récursive, la variable générique contiendra l'intégralité du segment de chemin correspondant, même si le document se trouve dans une sous-collection profondément imbriquée. Par exemple, les règles énumérées ci - dessus correspondent seraient un document situé dans /cities/SF/landmarks/coit_tower , et la valeur du document de variable serait SF/landmarks/coit_tower .

Notez, cependant, que le comportement des caractères génériques récursifs dépend de la version des règles.

Version 1

Les règles de sécurité utilisent la version 1 par défaut. Dans la version 1, les caractères génériques récursifs correspondent à un ou plusieurs éléments de chemin. Ils ne correspondent pas à un chemin vide, donc match /cities/{city}/{document=**} correspond à des documents dans les sous - collections , mais pas dans les cities collection, alors que match /cities/{document=**} correspond à la fois les documents du cities collection et sous - collections.

Les caractères génériques récursifs doivent figurer à la fin d'une instruction de correspondance.

Version 2

Dans la version 2 des règles de sécurité, les caractères génériques récursifs correspondent à zéro ou plusieurs éléments de chemin. match/cities/{city}/{document=**} correspond à des documents dans toutes les sous - collections ainsi que des documents dans les cities collection.

Vous devez opt-in pour la version 2 en ajoutant rules_version = '2'; en haut de vos règles de sécurité :

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

Vous pouvez avoir au plus un caractère générique récursif par déclaration de correspondance, mais dans la version 2, vous pouvez placer ce caractère générique n'importe où dans la déclaration de correspondance. Par exemple:

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 vous utilisez des requêtes de groupe de collecte , vous devez utiliser la version 2, voir la sécurisation des requêtes de groupe de collecte .

Chevauchement des déclarations de correspondance

Il est possible pour un document pour correspondre à plus d'un match de déclaration. Dans le cas où plusieurs allow expressions correspondent à une demande, l'accès est autorisé si l' une des conditions est true :

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

Dans l'exemple ci - dessus, toutes les lectures et les écritures cities collection seront autorisés parce que la deuxième règle est toujours true , même si la première règle est toujours false .

Limites des règles de sécurité

Lorsque vous travaillez avec des règles de sécurité, notez les limites suivantes :

Limite Des détails
Le nombre maximum de exists() , get() et getAfter() appelle par demande
  • 10 pour les demandes de document unique et les demandes de requête.
  • 20 pour les lectures multi-documents, les transactions et les écritures par lots. La limite précédente de 10 s'applique également à chaque opération.

    Par exemple, imaginez que vous créez une demande d'écriture groupée avec 3 opérations d'écriture et que vos règles de sécurité utilisent 2 appels d'accès au document pour valider chaque écriture. Dans ce cas, chaque écriture utilise 2 de ses 10 appels d'accès et la demande d'écriture par lots utilise 6 de ses 20 appels d'accès.

Le dépassement de l'une ou l'autre limite entraîne une erreur d'autorisation refusée.

Certains appels d'accès aux documents peuvent être mis en cache et les appels mis en cache ne sont pas pris en compte dans les limites.

Maximum imbriqué match de la profondeur de l' instruction dix
Longueur maximale du chemin, dans des segments de chemin, a permis dans un ensemble de imbriquées match déclarations 100
Le nombre maximum de chemins variables de capture autorisée dans un ensemble de imbriquées match déclarations 20
Profondeur maximale d'appel de fonction 20
Nombre maximum d'arguments de fonction 7
Nombre maximum de let liaisons variables par fonction dix
Nombre maximum d'appels de fonction récursifs ou cycliques 0 (non autorisé)
Nombre maximum d'expressions évaluées par requête 1 000
Taille maximale d'un ensemble de règles Les ensembles de règles doivent obéir à deux limites de taille :
  • une limite de 256 Ko de la taille de la source de texte publié à partir de la ruleset console Firebase ou de la CLI à l' aide firebase deploy .
  • une limite de 250 Ko sur la taille de l'ensemble de règles compilé qui se produit lorsque Firebase traite la source et la rend active sur le back-end.

Stockage en ligne

Structure basique

Les règles de sécurité Firebase dans Cloud Firestore et Cloud Storage utilisent la structure et la syntaxe suivantes :

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

Les concepts clés suivants sont importants à comprendre lorsque vous créez les règles :

  • Demande: La méthode ou les méthodes invoquées dans la allow déclaration. Ce sont des méthodes que vous autorisez à exécuter. Les méthodes standard sont: get , list , create , update à delete update et delete . Les read et write méthodes pratiques permettent un large accès de lecture et d' écriture sur la base de données ou d'une trajectoire de stockage.
  • Path: L'emplacement de base de données ou de stockage, représentée comme un chemin d' accès d' URI.
  • Règle: La allow déclaration, qui comprend une condition qui permet une demande si elle est évaluée à true.

Chemins correspondants

Cloud Storage Règles de sécurité match aux chemins de fichiers utilisés pour accéder à des fichiers dans Cloud Storage. Les règles peuvent match à des chemins précis ou des chemins génériques, et les règles peuvent également être imbriquées. Si aucune règle de correspondance permet une méthode de requête, ou la condition est false , la demande est refusée.

Correspondances exactes

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

Correspondances imbriquées

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

Matchs génériques

Les règles peuvent également être utilisés pour match à un modèle utilisant des caractères génériques. Un caractère générique est une variable nommée qui représente soit une chaîne unique tels que profilePhoto.png ou plusieurs segments de trajectoire, comme images/profilePhoto.png .

Un caractère générique est créé en ajoutant des accolades autour du nom générique, comme {string} . Un caractère générique segment multiple peut être déclaré en ajoutant =** au nom générique, comme {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 plusieurs règles correspondent à un fichier, le résultat est le OR du résultat de toutes les évaluations des règles. Autrement dit, si une règle du fichier correspond à sévalue true , le résultat est true .

Dans les règles ci - dessus, le fichier « images / profilePhoto.png » peuvent être lus si l' condition ou other_condition évaluer true, alors que le fichier « images / utilisateurs / utilisateur: 12345 / profilePhoto.png » est uniquement sous réserve du résultat de other_condition .

Une variable générique peut être référencé à partir du match de fournir le nom de fichier ou une autorisation de chemin:

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

Les règles de sécurité Cloud Storage ne sont pas en cascade et les règles ne sont évaluées que lorsque le chemin de la demande correspond à un chemin avec les règles spécifiées.

Demander une évaluation

Uploads, des téléchargements, des changements de métadonnées, et les suppressions sont évaluées selon la request envoyée au Cloud Storage. La request variable contient le chemin du fichier dans lequel la demande est en cours d' exécution, le moment où la demande est reçue, et la nouvelle resource de valeur si la demande est une écriture. Les en-têtes HTTP et l'état d'authentification sont également inclus.

La request objet contient également unique ID de l'utilisateur et la charge utile d' authentification Firebase dans le request.auth objet, qui sera expliqué plus loin dans l' authentification section de la documentation.

Une liste complète des propriétés dans la request objet est disponible ci - dessous:

Biens Taper La description
auth map<chaîne, chaîne> Lorsqu'un utilisateur est connecté, fournit uid , ID unique de l'utilisateur, et token , une carte des revendications Firebase authentification JWT. Dans le cas contraire, il sera null .
params map<chaîne, chaîne> Carte contenant les paramètres de requête de la requête.
path chemin Un path représentant le chemin de la demande est en cours d' exécution à.
resource map<chaîne, chaîne> La nouvelle valeur de ressources, présent uniquement sur write des demandes.
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, le téléchargement de fichiers avec certains types de contenu ou la suppression de fichiers supérieurs à une certaine taille.

Firebase Règles de sécurité pour Cloud Storage fournit des métadonnées de fichier dans la resource objet qui contient des paires de clés de valeur / des métadonnées refait surface dans un objet Cloud Storage. Ces propriétés peuvent être inspectés en read ou write des demandes pour assurer l' intégrité des données.

En write des demandes (telles que les ajouts, les mises à jour des métadonnées, et les suppressions), en plus de la resource objet, qui contient des métadonnées de fichier pour le fichier qui existe actuellement sur le chemin de demande, vous avez également la possibilité d'utiliser le request.resource objet, 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 de fichier.

Une liste complète des propriétés dans la resource objet est disponible ci - dessous:

Biens Taper La description
name chaîne de caractères Le nom complet de l'objet
bucket chaîne de caractères Le nom du bucket dans lequel réside cet objet.
generation entier La génération d'objets Google Cloud Storage de cet objet.
metageneration entier L' objet Google Cloud Storage metageneration de cet objet.
size entier 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 de caractères Un hachage MD5 de l'objet.
crc32c chaîne de caractères Un hachage crc32c de l'objet.
etag chaîne de caractères L'etag associé à cet objet.
contentDisposition chaîne de caractères La disposition du contenu associée à cet objet.
contentEncoding chaîne de caractères L'encodage du contenu associé à cet objet.
contentLanguage chaîne de caractères La langue de contenu associée à cet objet.
contentType chaîne de caractères Le type de contenu associé à cet objet.
metadata map<chaîne, chaîne> 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 à updated .

Exemple complet

En mettant tout cela ensemble, vous pouvez créer un exemple complet de règles pour une solution de stockage d'images :

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 données en temps réel

Structure basique

Dans Realtime Database, les règles de sécurité Firebase se composent d'expressions de type JavaScript contenues dans un document JSON.

Ils utilisent la syntaxe suivante :

{
  "rules": {
    "<<path>>": {
    // Allow the request if the condition for each method is true.
      ".read": <<condition>>,
      ".write": <<condition>>,
      ".validate": <<condition>>
    }
  }
}

Il y a trois éléments de base dans la règle :

  • Chemin: L'emplacement de la base de données. Cela reflète la structure JSON de votre base de données.
  • Demande: Ce sont les méthodes les utilisations des règles pour accorder l' accès. Les read et write des règles accordent une large lecture et l' accès en écriture, alors que validate règles agissent comme vérification secondaire à l' accès aux subventions en fonction des données entrantes ou existantes.
  • Condition: La condition qui permet une demande si elle est évaluée à true.

Comment les règles s'appliquent aux chemins

Dans la base de données en temps réel, les règles s'appliquent de manière atomique, ce qui signifie que les règles des nœuds parents de niveau supérieur remplacent les règles des nœuds enfants plus granulaires et que les règles d'un nœud plus profond ne peuvent pas accorder l'accès à un chemin parent. Vous ne pouvez pas affiner ou révoquer l'accès à un chemin plus profond dans votre structure de base de données si vous l'avez déjà accordé pour l'un des chemins parents.

Tenez compte des règles suivantes :

{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          // ignored, since read was allowed already
          ".read": false
        }
     }
  }
}

Cette structure de sécurité permet /bar/ à lire à partir de chaque fois /foo/ contient un enfant baz avec la valeur true . Le ".read": false règle sous /foo/bar/ n'a pas d' effet ici, puisque l' accès ne peut être révoqué par un chemin d'enfant.

Bien que cela puisse ne pas sembler immédiatement intuitif, il s'agit d'une partie puissante du langage de règles et permet d'implémenter des privilèges d'accès très complexes avec un effort minimal. Ceci est particulièrement utile pour la sécurité basée sur l'utilisateur .

Cependant, .validate règles ne sont pas en cascade. Toutes les règles de validation doivent être satisfaites à tous les niveaux de la hiérarchie pour qu'une écriture soit autorisée.

De plus, comme les règles ne s'appliquent pas à un chemin parent, l'opération de lecture ou d'écriture échoue s'il n'y a pas de règle à l'emplacement demandé ou à un emplacement parent qui accorde l'accès. Même si chaque chemin enfant affecté est accessible, la lecture à l'emplacement parent échouera complètement. Considérez cette structure :

{
  "rules": {
    "records": {
      "rec1": {
        ".read": true
      },
      "rec2": {
        ".read": false
      }
    }
  }
}

Sans comprendre que les règles sont évaluées atomiquement, il peut sembler aller chercher le /records/ chemin retournerait rec1 mais pas rec2 . Le résultat réel, cependant, est une erreur :

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
});
Objectif c
Note: Ce produit Firebase n'est pas disponible sur la cible Clip App.
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
}];
Rapide
Note: Ce produit Firebase n'est pas disponible sur la cible Clip App.
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
  });
});
DU REPOS
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Depuis l'opération de lecture à /records/ est atomique, et il n'y a pas de règle de lecture qui donne accès à toutes les données sous /records/ , cela lancera une PERMISSION_DENIED erreur. Si nous évaluons cette règle dans le simulateur de sécurité dans notre console Firebase , nous pouvons voir que l'opération de lecture a été refusée:

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

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

L'opération a été refusée car aucune règle de lecture a permis l' accès au /records/ chemin, mais notez que la règle de rec1 n'a jamais été évaluée parce qu'il n'a pas été dans le chemin que nous avons demandé. Pour chercher rec1 , nous aurions besoin d'y accéder directement:

JavaScript
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
Objectif c
Note: Ce produit Firebase n'est pas disponible sur la cible Clip App.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Rapide
Note: Ce produit Firebase n'est pas disponible sur la cible Clip App.
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
  }
});
DU REPOS
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Variable d'emplacement

En temps réel les règles de base de données prennent en charge un $location variable pour correspondre à des segments de chemin. Utilisez le $ préfixe devant votre segment de chemin pour correspondre à votre règle à tous les nœuds enfants le long du chemin.

  {
    "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')"
          }
        }
      }
    }
  }

Vous pouvez également utiliser la $variable en parallèle avec des noms de chemin constant.

  {
    "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 }
      }
    }
  }