Validation des données

Vous pouvez utiliser Firebase Security Rules pour écrire de nouvelles données de manière conditionnelle en fonction des données existantes de votre base de données ou de votre bucket de stockage. Vous pouvez également écrire des règles qui appliquent des validations de données en limitant les écritures en fonction des nouvelles données écrites. Pour en savoir plus sur les règles qui utilisent des données existantes pour créer des conditions de sécurité, lisez la suite.

Sélectionnez un produit dans chaque section pour en savoir plus sur les règles de validation des données.

Restrictions concernant les nouvelles données

Cloud Firestore

Si vous souhaitez vous assurer qu'un document contenant un champ spécifique n'est pas créé, vous pouvez inclure le champ dans la condition allow. Par exemple, si vous souhaitez refuser la création de documents contenant le champ ranking, vous devez l'interdire dans la condition create.

  service cloud.firestore {
    match /databases/{database}/documents {
      // Disallow
      match /cities/{city} {
        allow create: if !("ranking" in request.resource.data)
      }
    }
  }

Realtime Database

Si vous souhaitez vous assurer que les données contenant certaines valeurs ne sont pas ajoutées à votre base de données, vous devez inclure cette valeur dans vos règles et l'interdire pour les écritures. Par exemple, si vous souhaitez refuser toutes les écritures contenant des valeurs ranking, vous devez refuser les écritures pour tous les documents contenant des valeurs ranking.

  {
    "rules": {
      // Write is allowed for all paths
      ".write": true,
      // Allows writes only if new data doesn't include a `ranking` child value
      ".validate": "!newData.hasChild('ranking')
    }
  }

Cloud Storage

Si vous souhaitez vous assurer qu'un fichier contenant des métadonnées spécifiques n'est pas créé, vous pouvez inclure les métadonnées dans la condition allow. Par exemple, si vous souhaitez refuser la création de fichiers contenant des métadonnées ranking, vous devez l'interdire dans la condition create.

  service firebase.storage {
    match /b/{bucket}/o {
      match /files/{allFiles=**} {
      // Disallow
        allow create: if !("ranking" in request.resource.metadata)
      }
    }
  }

Utiliser les données existantes dans Firebase Security Rules

Cloud Firestore

De nombreuses applications stockent des informations de contrôle des accès sous forme de champs dans les documents de la base de données. Cloud Firestore Security Rules peut autoriser ou refuser l'accès de manière dynamique en fonction des données du document:

  service cloud.firestore {
    match /databases/{database}/documents {
      // Allow the user to read data if the document has the 'visibility'
      // field set to 'public'
      match /cities/{city} {
        allow read: if resource.data.visibility == 'public';
      }
    }
  }

La variable resource fait référence au document demandé et resource.data correspond à une carte de tous les champs et valeurs stockés dans le document. Pour plus d'informations sur la variable resource, consultez la documentation de référence.

Lorsque vous écrivez des données, vous pouvez comparer les données entrantes aux données existantes. Vous pouvez ainsi vous assurer qu'un champ n'a pas changé, qu'il n'a augmenté que d'une unité ou que la nouvelle valeur est prévue au moins une semaine plus tard. Dans ce cas, si votre ensemble de règles autorise l'écriture en attente, la variable request.resource contient l'état futur du document. Pour les opérations update qui ne modifient qu'un sous-ensemble de champs du document, la variable request.resource contient l'état du document en attente après l'opération. Vous pouvez vérifier les valeurs des champs dans request.resource pour éviter les mises à jour de données non souhaitées ou incohérentes :

   service cloud.firestore {
     match /databases/{database}/documents {
      // Make sure all cities have a positive population and
      // the name is not changed
      match /cities/{city} {
        allow update: if request.resource.data.population > 0
                      && request.resource.data.name == resource.data.name;
      }
    }
  }

Realtime Database

Dans Realtime Database, utilisez des règles .validate pour appliquer des structures de données et valider le format et le contenu des données. Rules exécute les règles .validate après avoir vérifié qu'une règle .write accorde l'accès.

Les règles .validate ne sont pas en cascade. Si une règle de validation échoue pour un chemin ou un sous-chemin de la règle, l'ensemble de l'opération d'écriture est rejeté. De plus, les définitions de validation ne vérifient que les valeurs non nulles et ignorent ensuite toutes les requêtes qui suppriment des données.

Prenons les règles .validate suivantes:

  {
    "rules": {
      // write is allowed for all paths
      ".write": true,
      "widget": {
        // a valid widget must have attributes "color" and "size"
        // allows deleting widgets (since .validate is not applied to delete rules)
        ".validate": "newData.hasChildren(['color', 'size'])",
        "size": {
          // the value of "size" must be a number between 0 and 99
          ".validate": "newData.isNumber() &&
                        newData.val() >= 0 &&
                        newData.val() <= 99"
        },
        "color": {
          // the value of "color" must exist as a key in our mythical
          // /valid_colors/ index
          ".validate": "root.child('valid_colors/' + newData.val()).exists()"
        }
      }
    }
  }

Les requêtes d'écriture dans une base de données avec les règles ci-dessus auraient les résultats suivants:

JavaScript
var ref = db.ref("/widget");

// PERMISSION_DENIED: does not have children color and size
ref.set('foo');

// PERMISSION DENIED: does not have child color
ref.set({size: 22});

// PERMISSION_DENIED: size is not a number
ref.set({ size: 'foo', color: 'red' });

// SUCCESS (assuming 'blue' appears in our colors list)
ref.set({ size: 21, color: 'blue'});

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child('size').set(99);
Objective-C
Remarque:Ce produit Firebase n'est pas disponible sur la cible App Clip.
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"];

// PERMISSION_DENIED: does not have children color and size
[ref setValue: @"foo"];

// PERMISSION DENIED: does not have child color
[ref setValue: @{ @"size": @"foo" }];

// PERMISSION_DENIED: size is not a number
[ref setValue: @{ @"size": @"foo", @"color": @"red" }];

// SUCCESS (assuming 'blue' appears in our colors list)
[ref setValue: @{ @"size": @21, @"color": @"blue" }];

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
[[ref child:@"size"] setValue: @99];
Swift
Remarque:Ce produit Firebase n'est pas disponible sur la cible App Clip.
var ref = FIRDatabase.database().reference().child("widget")

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo")

// PERMISSION DENIED: does not have child color
ref.setValue(["size": "foo"])

// PERMISSION_DENIED: size is not a number
ref.setValue(["size": "foo", "color": "red"])

// SUCCESS (assuming 'blue' appears in our colors list)
ref.setValue(["size": 21, "color": "blue"])

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("widget");

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo");

// PERMISSION DENIED: does not have child color
ref.child("size").setValue(22);

// PERMISSION_DENIED: size is not a number
Map<String,Object> map = new HashMap<String, Object>();
map.put("size","foo");
map.put("color","red");
ref.setValue(map);

// SUCCESS (assuming 'blue' appears in our colors list)
map = new HashMap<String, Object>();
map.put("size", 21);
map.put("color","blue");
ref.setValue(map);

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
REST
# PERMISSION_DENIED: does not have children color and size
curl -X PUT -d 'foo' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION DENIED: does not have child color
curl -X PUT -d '{"size": 22}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION_DENIED: size is not a number
curl -X PUT -d '{"size": "foo", "color": "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# SUCCESS (assuming 'blue' appears in our colors list)
curl -X PUT -d '{"size": 21, "color": "blue"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# If the record already exists and has a color, this will
# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
# will fail to validate
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Cloud Storage

Lorsque vous évaluez des règles, vous pouvez également évaluer les métadonnées du fichier importé, téléchargé, modifié ou supprimé. Vous pouvez ainsi créer des règles complexes et puissantes qui, par exemple, n'autorisent que les fichiers de certains types de contenu à être importés ou ne permettent de supprimer que les fichiers de plus d'une certaine taille.

L'objet resource contient des paires clé/valeur avec des métadonnées de fichier affichées dans un objet Cloud Storage. Ces propriétés peuvent être inspectées sur les requêtes read ou write pour garantir l'intégrité des données. L'objet resource vérifie les métadonnées des fichiers existants dans votre bucket Cloud Storage.

  service firebase.storage {
    match /b/{bucket}/o {
      match /images {
        match /{allImages=**} {
          // Allow reads if a custom 'visibility' field is set to 'public'
          allow read: if resource.metadata.visibility == 'public';
        }
      }
    }
  }

Vous pouvez également utiliser l'objet request.resource pour les requêtes write (telles que les importations, les mises à jour de métadonnées et les suppressions). L'objet request.resource obtient les métadonnées du fichier qui seront écrites si write est autorisé.

Vous pouvez utiliser ces deux valeurs pour empêcher les mises à jour indésirables ou incohérentes, ou pour appliquer des contraintes d'application, telles que le type ou la taille de fichier.

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

Vous trouverez la liste complète des propriétés de l'objet resource dans la documentation de référence.