La sécurité peut être l'une des pièces les plus complexes du jeu de réflexion sur le 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).
Firebase Security Rules supprime la couche intermédiaire (serveur) et vous permet de spécifier la couche du chemin d'accès les autorisations pour les clients qui se connectent directement à vos données. Référez-vous à ce guide pour : découvrez comment les règles sont appliquées aux requêtes entrantes.
Sélectionnez un produit pour en savoir plus sur ses règles.
Cloud Firestore
Structure de base
Firebase Security Rules dans Cloud Firestore et Cloud Storage utilisent la structure suivante : syntaxe:
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
allow <<methods>> : if <<condition>>
}
}
Il est important de comprendre les concepts clés suivants lorsque vous créez ces règles:
- Requête:la ou les méthodes appelées dans l'instruction
allow
. Il s'agit que vous autorisez l'exécution. Les méthodes standards sont les suivantes :get
,list
,create
,update
etdelete
. Méthodes pratiquesread
etwrite
permettent un accès étendu en lecture et en écriture sur le chemin de base de données ou de stockage spécifié. - Chemin d'accès:la base de données ou l'emplacement de stockage, représenté sous forme de chemin d'URI.
- Règle:l'instruction
allow
, qui inclut une condition qui autorise une s'il renvoie la valeur "true".
Règles de sécurité version 2
Depuis mai 2019, la version 2 des règles de sécurité Firebase est
disponibles. La version 2 de ces règles modifie le comportement des fonctions récursives
caractères génériques {name=**}
. Vous devez utiliser la version 2 si vous prévoyez de le faire
Utilisez des requêtes de groupe de collections. Vous devez activer les
version 2 en faisant de rules_version = '2';
la première ligne de votre code de sécurité
règles:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
Chemins correspondants
Toutes les instructions de correspondance doivent pointer vers des documents, et non vers des collections. Une instruction de correspondance peut pointer vers un document spécifique, comme dans match /cities/SF
, ou utiliser des caractères génériques pour pointer vers un document du chemin spécifié, comme dans match /cities/{city}
.
Dans l'exemple ci-dessus, l'instruction de correspondance utilise la syntaxe de caractère générique {city}
.
Cela signifie que la règle s'applique à tous les documents de la collection cities
, tels que /cities/SF
ou /cities/NYC
. Lorsque les expressions allow
dans l'instruction de correspondance sont évaluées, la résolution de la variable city
donne le nom de document de ville, tel que SF
ou NYC
.
Sous-collections correspondantes
Les données dans Cloud Firestore sont organisées en collections de documents, et chacune 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érarchisées.
Examinons la situation dans laquelle chaque document de la collection cities
contient une sous-collection landmarks
. Les règles de sécurité ne s'appliquent qu'au chemin correspondant. Par conséquent, les contrôles d'accès définis dans la collection cities
ne s'appliquent pas à la sous-collection landmarks
. À 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 des instructions match
, le chemin de l'instruction interne match
est toujours relatif au chemin de l'instruction externe match
. 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 souhaitez que les règles s'appliquent à une hiérarchie arbitrairement profonde, utilisez la fonction
syntaxe de caractère 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>;
}
}
}
Lorsque vous utilisez la syntaxe de caractères génériques récursifs, la variable générique contient 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 répertoriées ci-dessus correspondent à un document situé à l'adresse /cities/SF/landmarks/coit_tower
et la valeur de la variable document
sera SF/landmarks/coit_tower
.
Notez toutefois 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, match /cities/{city}/{document=**}
correspond donc aux documents des sous-collections, mais pas dans la collection cities
alors que match /cities/{document=**}
correspond aux documents de la collection cities
et des sous-collections.
Les caractères génériques récursifs doivent apparaître à 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 peuvent correspondre à zéro, un ou plusieurs éléments de chemin. match/cities/{city}/{document=**}
correspond aux documents des sous-collections, ainsi qu'aux documents de la collection cities
.
Vous devez activer 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 utiliser au maximum un caractère générique récursif par requête, mais dans la version 2, vous pouvez placer ce caractère générique n'importe où dans l'instruction de correspondance. 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 groupes de collections, vous devez utiliser la version 2. Consultez la page Sécuriser les requêtes de groupes de collections.
Chevauchement d'instructions de correspondance
Il est possible qu'un document corresponde à plusieurs instructions match
. Dans le cas où plusieurs expressions allow
correspondent à une requête, l'accès est autorisé si au moins une des conditions est définie sur 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 écritures dans la collection cities
seront autorisées, car la seconde 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 utilisez des règles de sécurité, tenez compte des limites suivantes :
Limite | Détails |
---|---|
Nombre maximal d'appels de méthode exists() , get() et getAfter() par requête |
Le dépassement de l'une ou l'autre limite entraîne une erreur de type "permission refusée". Certains appels d'accès aux documents peuvent être mis en cache, et les appels en cache ne sont pas pris en compte dans les limites. |
Profondeur maximale d'instructions match imbriquées |
10 |
Longueur maximale du chemin, en segments de chemin, autorisée dans un ensemble d'instructions match imbriquées |
100 |
Nombre maximal de variables de capture de chemin autorisées dans un ensemble d'instructions match imbriquées |
20 |
Profondeur maximale des appels de fonction | 20 |
Nombre maximal d'arguments de fonction | 7 |
Nombre maximal de liaisons de variables let par fonction |
10 |
Nombre maximal d'appels de fonction récursifs ou cycliques | 0 (non autorisé) |
Nombre maximal d'expressions évaluées par requête | 1 000 |
Taille maximale d'un ensemble de règles | Les ensembles de règles doivent respecter deux limites de taille :
|
Cloud Storage
Structure de base
Firebase Security Rules dans Cloud Firestore et Cloud Storage utilisent la structure suivante : syntaxe:
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
allow <<methods>> : if <<condition>>
}
}
Il est important de comprendre les concepts clés suivants lorsque vous créez ces règles:
- Requête:la ou les méthodes appelées dans l'instruction
allow
. Il s'agit que vous autorisez l'exécution. Les méthodes standards sont les suivantes :get
,list
,create
,update
etdelete
. Méthodes pratiquesread
etwrite
permettent un accès étendu en lecture et en écriture sur le chemin de base de données ou de stockage spécifié. - Chemin d'accès:la base de données ou l'emplacement de stockage, représenté sous forme de chemin d'URI.
- Règle:l'instruction
allow
, qui inclut une condition qui autorise une s'il renvoie la valeur "true".
Chemins correspondants
Cloud Storage Security Rules match
les chemins d'accès utilisés pour accéder aux fichiers dans
Cloud Storage Les règles peuvent match
des chemins d'accès exacts ou des chemins d'accès génériques.
peuvent également être imbriquées. Si aucune règle de correspondance n'autorise une méthode de requête, ou si la
prend la valeur false
, la requête 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>; } }
Correspondances de caractères génériques
Les règles peuvent également être utilisées pour match
un modèle à l'aide de caractères génériques. Un caractère générique est un
variable nommée qui représente une seule chaîne, telle que
profilePhoto.png
, ou plusieurs segments de chemin d'accès, tels que
images/profilePhoto.png
Un caractère générique est créé en ajoutant des accolades autour du nom générique, comme
{string}
Vous pouvez déclarer un caractère générique pour plusieurs segments en ajoutant =**
à la
un 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
des évaluations de règles. Autrement dit, si une règle correspondante au fichier renvoie la valeur true
,
le résultat est true
.
Dans les règles ci-dessus, le fichier "images/profilePhoto.png" peut être lu si
condition
ou other_condition
a la valeur "true", tandis que le fichier
"images/users/user:12345/profilePhoto.png" n'est sujette qu'au résultat
other_condition
Une variable générique peut être référencée à partir de l'autorisation de nom de fichier ou de chemin d'accès match
:
// Another way to restrict the name of a file match /images/{imageId} { allow read: if imageId == "profilePhoto.png"; }
Les Cloud Storage Security Rules ne sont pas en cascade, et les règles ne sont évaluées que lorsque l'attribut chemin de requête correspond à un chemin avec des règles spécifiées.
Demander une évaluation
Les importations, les téléchargements, les modifications de métadonnées et les suppressions sont évalués à l'aide du
request
envoyés à Cloud Storage. La variable request
contient
le chemin d'accès au 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 requête est une écriture. Les en-têtes HTTP et l'état d'authentification sont également inclus.
L'objet request
contient également l'identifiant unique de l'utilisateur et le
la charge utile Firebase Authentication dans l'objet request.auth
, qui sera
expliqué plus en détail dans la section Authentification
de la documentation.
Vous trouverez ci-dessous la liste complète des propriétés de l'objet request
:
Propriété | Type | Description |
---|---|---|
auth |
map<chaîne, chaîne> | Lorsqu'un utilisateur est connecté, fournit uid , son identifiant unique et
token , une carte de Firebase Authentication revendications JWT. Sinon, il est défini sur null . |
params |
map<chaîne, chaîne> | Carte contenant les paramètres de la requête. |
path |
chemin d'accès | path représentant le chemin d'accès de la requête. |
resource |
map<chaîne, chaîne> | Nouvelle valeur de ressource, présente uniquement dans les requêtes write .
|
time |
timestamp | Code temporel représentant l'heure du serveur à laquelle la requête est évaluée. |
Évaluation des ressources
Lors de l'évaluation des règles, vous pouvez également évaluer les métadonnées du fichier en cours d'importation, de téléchargement, de modification ou de suppression. Cela vous permet de créer des règles complexes et performantes, par exemple, n'autoriser que les fichiers contenant de types de contenu à importer, ou uniquement les fichiers dont la taille dépasse un certain seuil supprimés.
Firebase Security Rules pour Cloud Storage fournit des métadonnées de fichier dans resource
qui contient des paires clé/valeur des métadonnées affichées dans
Cloud Storage. Ces propriétés peuvent être inspectées sur read
ou
write
pour garantir l'intégrité des données.
Pour les requêtes write
(telles que les importations, les mises à jour de métadonnées et les suppressions), en plus de l'objet resource
, qui contient les métadonnées du fichier existant actuellement sur le chemin de la requête, vous pouvez également 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 vous assurer que les données
l'intégrité ou appliquer des contraintes d'application telles que le type ou la taille du fichier.
Vous trouverez ci-dessous la liste complète des propriétés de l'objet resource
:
Propriété | Type | Description |
---|---|---|
name |
chaîne | Le nom complet de l'objet |
bucket |
chaîne | Nom du bucket dans lequel se trouve cet objet. |
generation |
int | Génération de l'objet Google Cloud Storage de cet objet. |
metageneration |
int | Le Google Cloud Storage métagénération de cet objet. |
size |
int | La taille de l'objet en octets. |
timeCreated |
timestamp | Code temporel représentant l'heure de création d'un objet. |
updated |
timestamp | Code temporel représentant l'heure de la dernière mise à jour d'un objet. |
md5Hash |
chaîne | Hachage MD5 de l'objet. |
crc32c |
chaîne | Hachage crc32c de l'objet. |
etag |
chaîne | ETag associé à cet objet. |
contentDisposition |
chaîne | Disposition du contenu associée à cet objet. |
contentEncoding |
chaîne | Encodage du contenu associé à cet objet. |
contentLanguage |
chaîne | Langue du contenu associée à cet objet. |
contentType |
chaîne | 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
.
Limites des règles de sécurité
Lorsque vous utilisez des règles de sécurité, tenez compte des limites suivantes :
Limite | Détails |
---|---|
Nombre maximal d'appels firestore.exists() et firestore.get() par requête |
2 pour les requêtes de documents uniques et les requêtes de type "query". Le dépassement de cette limite entraîne une erreur d'autorisation refusée. Les appels d'accès aux mêmes documents peuvent être mis en cache, et les appels en cache ne sont pas pris en compte dans les limites. |
Exemple complet
En combinant toutes ces données, vous pouvez créer un exemple complet de règles pour une image. solution de stockage:
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 } } } }
Realtime Database
Structure de base
Dans Realtime Database, Firebase Security Rules se compose d'expressions semblables à JavaScript contenues dans un document JSON.
Elles utilisent la syntaxe suivante :
{
"rules": {
"<<path>>": {
// Allow the request if the condition for each method is true.
".read": <<condition>>,
".write": <<condition>>,
".validate": <<condition>>
}
}
}
La règle comporte trois éléments de base:
- Chemin d'accès : emplacement de la base de données. Cela reflète la structure JSON de votre base de données.
- Requête:il s'agit des méthodes utilisées par la règle pour accorder l'accès.
read
etwrite
accordent un accès étendu en lecture et en écriture, tandis que les règlesvalidate
servent de vérification secondaire pour accorder l'accès en fonction des requêtes entrantes données. - Condition:condition qui autorise une requête si elle renvoie la valeur "true".
Application des règles aux chemins
Dans Realtime Database, Rules s'applique de manière atomique, ce qui signifie que les règles au niveau les nœuds parents de niveau supérieur remplacent les règles au niveau des nœuds enfants plus précis les règles d'un nœud plus profond ne peuvent pas accorder l'accès à un chemin parent. Toi ne peut pas affiner ni révoquer l'accès à un chemin d'accès 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 de lire /bar/
à tout moment
/foo/
contient un baz
enfant avec la valeur true
.
La règle ".read": false
sous /foo/bar/
n'a aucune
car l'accès ne peut pas être révoqué par un chemin d'accès enfant.
Bien que cela ne semble pas immédiatement intuitif, cet aspect est essentiel et permet d'implémenter des privilèges d'accès très complexes avec un minimum d'efforts. Cela est particulièrement utile pour la sécurité basée sur l'utilisateur.
Cependant, les règles .validate
ne sont pas appliquées en cascade. Toutes les règles de validation
doit être satisfaite à 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 d'accès parent, les accès en lecture et en écriture L'opération échoue s'il n'existe aucune règle à l'emplacement demandé ou au niveau d'un parent. l'emplacement qui accorde l'accès. Même si tous les chemins d'accès enfants concernés sont accessibles, la lecture à l'emplacement parent échouera complètement. Prenons cette structure:
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
Sans comprendre que les règles sont évaluées de manière atomique, il peut sembler
Par exemple, si vous récupérez le chemin d'accès /records/
, vous obtiendrez rec1
mais pas rec2
. Toutefois, le résultat réel 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 });
Objective-C
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 }];
Swift
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 }); });
REST
curl https://docs-examples.firebaseio.com/rest/records/ # response returns a PERMISSION_DENIED error
Étant donné que l'opération de lecture sur /records/
est atomique et qu'aucune règle de lecture n'accorde l'accès à toutes les données sous /records/
, une erreur PERMISSION_DENIED
est générée. Si nous évaluons cette règle dans le simulateur de sécurité de notre console Firebase, nous constatons 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 n'autorise l'accès au chemin d'accès /records/
. Notez toutefois que la règle pour rec1
n'a jamais été évaluée, car elle ne se trouvait pas dans le chemin que nous avons demandé. Pour récupérer rec1
, nous devons 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 });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Swift
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 } });
REST
curl https://docs-examples.firebaseio.com/rest/records/rec1 # SUCCESS!
Variable de lieu
Les Rules Realtime Database prennent en charge un $location
pour faire correspondre les segments de chemin d'accès. Utilisez le préfixe $
devant le chemin d'accès
pour faire correspondre votre règle à tout nœud enfant 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 $variable
en parallèle avec des noms de chemin d'accès constants.
{
"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 }
}
}
}