Google is committed to advancing racial equity for Black communities. See how.
Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Verwenden Sie Bedingungen in Echtzeit-Datenbankregeln

Dieses Handbuch baut auf dem Kernhandbuch für Firebase-Sicherheitsregeln auf und zeigt, wie Sie Bedingungen zu Ihren Firebase-Echtzeitdatenbank-Sicherheitsregeln hinzufügen.

Der Hauptbaustein der Sicherheitsregeln für Echtzeitdatenbanken ist die Bedingung . Eine Bedingung ist ein boolescher Ausdruck, der bestimmt, ob eine bestimmte Operation zugelassen oder abgelehnt werden soll. Für Grundregeln funktioniert die Verwendung von true und false Literalen als Bedingungen perfekt. Mit der Sprache Realtime Database Security Rules können Sie jedoch komplexere Bedingungen schreiben, die Folgendes ermöglichen:

  • Überprüfen Sie die Benutzerauthentifizierung
  • Bewerten Sie vorhandene Daten anhand neu übermittelter Daten
  • Greifen Sie auf verschiedene Teile Ihrer Datenbank zu und vergleichen Sie sie
  • Eingehende Daten validieren
  • Verwenden Sie die Struktur eingehender Abfragen für die Sicherheitslogik

Verwenden von $ -Variablen zum Erfassen von Pfadsegmenten

Sie können Teile des Pfads zum Lesen oder Schreiben erfassen, indem Sie Erfassungsvariablen mit dem Präfix $ deklarieren. Dies dient als Platzhalter und speichert den Wert dieses Schlüssels zur Verwendung innerhalb der Regelbedingungen:

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

Die dynamischen $ -Variablen können auch parallel zu konstanten Pfadnamen verwendet werden. In diesem Beispiel verwenden wir die Variable $other , um eine .validate Regel zu deklarieren, die sicherstellt, dass das widget keine anderen .validate als title und color . Jedes Schreiben, das dazu führen würde, dass zusätzliche untergeordnete Elemente erstellt werden, schlägt fehl.

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

Authentifizierung

Eines der häufigsten Muster für Sicherheitsregeln ist die Steuerung des Zugriffs basierend auf dem Authentifizierungsstatus des Benutzers. Beispielsweise möchte Ihre App möglicherweise nur angemeldeten Benutzern erlauben, Daten zu schreiben.

Wenn Ihre App die Firebase-Authentifizierung verwendet, enthält die Variable request.auth die Authentifizierungsinformationen für den Client, der Daten anfordert. Weitere Informationen zu request.auth finden Sie in der Referenzdokumentation .

Die Firebase-Authentifizierung ist in die Firebase-Echtzeitdatenbank integriert, sodass Sie den Datenzugriff auf Benutzerbasis unter bestimmten Bedingungen steuern können. Sobald ein Benutzer authentifiziert, die auth wird Variable in der Realtime - Datenbank Sicherheitsregeln Regeln mit den Informationen des Benutzers aufgefüllt werden. Zu diesen Informationen gehören die eindeutige Kennung ( uid ) sowie verknüpfte Kontodaten wie eine Facebook-ID oder eine E-Mail-Adresse und andere Informationen. Wenn Sie einen benutzerdefinierten Authentifizierungsanbieter implementieren, können Sie der Authentifizierungsnutzlast Ihres Benutzers eigene Felder hinzufügen.

In diesem Abschnitt wird erläutert, wie Sie die Sprache der Firebase Realtime Database-Sicherheitsregeln mit Authentifizierungsinformationen zu Ihren Benutzern kombinieren. Durch die Kombination dieser beiden Konzepte können Sie den Zugriff auf Daten basierend auf der Benutzeridentität steuern.

Die auth Variable

Die vordefinierte auth Variable in den Regeln ist null, bevor die Authentifizierung stattfindet.

Sobald ein Benutzer mit der Firebase-Authentifizierung authentifiziert wurde , enthält er die folgenden Attribute:

Anbieter Die verwendete Authentifizierungsmethode ("Passwort", "anonym", "Facebook", "Github", "Google" oder "Twitter").
uid Eine eindeutige Benutzer-ID, die garantiert für alle Anbieter eindeutig ist.
Zeichen Der Inhalt des Firebase Auth ID-Tokens. Weitere Informationen finden Sie in der Referenzdokumentation zu auth.token .

Hier ist eine Beispielregel, die die auth Variable verwendet, um sicherzustellen, dass jeder Benutzer nur in einen benutzerspezifischen Pfad schreiben kann:

{
  "rules": {
    "users": {
      "$user_id": {
        // grants write access to the owner of this user account
        // whose uid must exactly match the key ($user_id)
        ".write": "$user_id === auth.uid"
      }
    }
  }
}

Strukturierung Ihrer Datenbank zur Unterstützung von Authentifizierungsbedingungen

In der Regel ist es hilfreich, Ihre Datenbank so zu strukturieren, dass das Schreiben von Regeln einfacher wird. Ein gängiges Muster zum Speichern von Benutzerdaten in der Echtzeitdatenbank besteht darin, alle Benutzer in einem einzelnen users zu speichern, dessen uid die uid Werte für jeden Benutzer sind. Wenn Sie den Zugriff auf diese Daten so einschränken möchten, dass nur der angemeldete Benutzer seine eigenen Daten sehen kann, sehen Ihre Regeln ungefähr so ​​aus.

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid"
      }
    }
  }
}

Arbeiten mit benutzerdefinierten Authentifizierungsansprüchen

Für Apps, die eine benutzerdefinierte Zugriffssteuerung für verschiedene Benutzer erfordern, können Entwickler mit der Firebase-Authentifizierung Ansprüche für einen Firebase-Benutzer festlegen . Diese Ansprüche sind in der Variable auth.token in Ihren Regeln auth.token . Hier ist ein Beispiel für Regeln, die den benutzerdefinierten Anspruch hasEmergencyTowel :

{
  "rules": {
    "frood": {
      // A towel is about the most massively useful thing an interstellar
      // hitchhiker can have
      ".read": "auth.token.hasEmergencyTowel === true"
    }
  }
}

Entwickler, die ihre eigenen benutzerdefinierten Authentifizierungstoken erstellen, können diesen Token optional Ansprüche hinzufügen. Diese Ansprüche sind für die Variable auth.token in Ihren Regeln auth.token .

Vorhandene Daten im Vergleich zu neuen Daten

Die vordefinierte data wird verwendet , um die Daten vor einer Schreiboperation beziehen erfolgt. Umgekehrt enthält die Variable newData die neuen Daten, die bei erfolgreichem Schreibvorgang vorhanden sind. newData repräsentiert das zusammengeführte Ergebnis der neuen Daten, die geschrieben werden, und der vorhandenen Daten.

Zur Veranschaulichung würde diese Regel es uns ermöglichen, neue Datensätze zu erstellen oder vorhandene zu löschen, aber keine Änderungen an vorhandenen Nicht-Null-Daten vorzunehmen:

// we can write as long as old data or new data does not exist
// in other words, if this is a delete or a create, but not an update
".write": "!data.exists() || !newData.exists()"

Referenzieren von Daten in anderen Pfaden

Beliebige Daten können als Kriterium für Regeln verwendet werden. Mit den vordefinierten Variablen root , data und newData können wir auf jeden Pfad zugreifen, der vor oder nach einem Schreibereignis vorhanden wäre.

Betrachten Sie dieses Beispiel, das Schreibvorgänge zulässt, solange der Wert des Knotens /allow_writes/ true , für den übergeordneten Knoten kein readOnly Flag gesetzt ist und in den neu geschriebenen Daten ein readOnly Namen foo vorhanden ist:

".write": "root.child('allow_writes').val() === true &&
          !data.parent().child('readOnly').exists() &&
          newData.child('foo').exists()"

Daten validieren

Das Erzwingen von Datenstrukturen und das Überprüfen des Formats und des Inhalts von Daten sollte mithilfe von .validate Regeln erfolgen, die erst ausgeführt werden, nachdem eine .write Regel erfolgreich Zugriff gewährt hat. Unten finden Sie eine Beispieldefinition für eine .validate Regel, die nur Datumsangaben im Format JJJJ-MM-TT zwischen den Jahren 1900-2099 zulässt, die mit einem regulären Ausdruck überprüft werden.

".validate": "newData.isString() &&
              newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"

Die .validate Regeln sind die einzigen Sicherheitsregeln, die nicht kaskadieren. Wenn eine Validierungsregel für einen untergeordneten Datensatz fehlschlägt, wird der gesamte Schreibvorgang abgelehnt. Darüber hinaus werden die Validierungsdefinitionen ignoriert, wenn Daten gelöscht werden (dh wenn der neu geschriebene Wert null ).

Dies mag trivial erscheinen, ist jedoch eine wichtige Funktion zum Schreiben leistungsfähiger Sicherheitsregeln für Firebase-Echtzeitdatenbanken. Beachten Sie die folgenden Regeln:

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

Sehen Sie sich unter Berücksichtigung dieser Variante die Ergebnisse für die folgenden Schreibvorgänge an:

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);
Ziel c
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];
Schnell
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);
SICH AUSRUHEN
# 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

Schauen wir uns nun dieselbe Struktur an, verwenden jedoch .write anstelle von .validate :

{
  "rules": {
    // this variant will NOT allow deleting records (since .write would be disallowed)
    "widget": {
      // a widget must have 'color' and 'size' in order to be written to this path
      ".write": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99, ONLY IF WE WRITE DIRECTLY TO SIZE
        ".write": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical valid_colors/ index
        // BUT ONLY IF WE WRITE DIRECTLY TO COLOR
        ".write": "root.child('valid_colors/'+newData.val()).exists()"
      }
    }
  }
}

In dieser Variante wäre eine der folgenden Operationen erfolgreich:

JavaScript
var ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.set({size: 99999, color: 'red'});

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child('size').set(99);
Ziel c
Firebase *ref = [[Firebase alloc] initWithUrl:URL];

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
[ref setValue: @{ @"size": @9999, @"color": @"red" }];

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
[[ref childByAppendingPath:@"size"] setValue: @99];
Schnell
var ref = Firebase(url:URL)

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.setValue(["size": 9999, "color": "red"])

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.childByAppendingPath("size").setValue(99)
Java
Firebase ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
Map<String,Object> map = new HashMap<String, Object>();
map.put("size", 99999);
map.put("color", "red");
ref.setValue(map);

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child("size").setValue(99);
SICH AUSRUHEN
# ALLOWED? Even though size is invalid, widget has children color and size,
# so write is allowed and the .write rule under color is ignored
curl -X PUT -d '{size: 99999, color: "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# ALLOWED? Works even if widget does not exist, allowing us to create a widget
# which is invalid and does not have a valid color.
# (allowed by the write rule under "color")
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Dies zeigt die Unterschiede zwischen .write und .validate Regeln. Wie gezeigt, sollten alle diese Regeln mit .validate geschrieben werden, mit der möglichen Ausnahme der newData.hasChildren() -Regel, die davon abhängt, ob Löschungen zulässig sind.

Abfragebasierte Regeln

Obwohl Sie Regeln nicht als Filter verwenden können , können Sie den Zugriff auf Teilmengen von Daten einschränken, indem Sie Abfrageparameter in Ihren Regeln verwenden. Verwenden Sie die query. Ausdrücke in Ihren Regeln, um Lese- oder Schreibzugriff basierend auf Abfrageparametern zu gewähren.

Die folgende abfragebasierte Regel verwendet beispielsweise benutzerbasierte Sicherheitsregeln und abfragebasierte Regeln, um den Zugriff auf Daten in der baskets nur auf die Einkaufskörbe zu beschränken, die der aktive Benutzer besitzt:

"baskets": {
  ".read": "auth.uid != null &&
            query.orderByChild == 'owner' &&
            query.equalTo == auth.uid" // restrict basket access to owner of basket
}

Die folgende Abfrage, die die Abfrageparameter in der Regel enthält, wäre erfolgreich:

db.ref("baskets").orderByChild("owner")
                 .equalTo(auth.currentUser.uid)
                 .on("value", cb)                 // Would succeed

Abfragen, die die Parameter nicht in der Regel enthalten, schlagen jedoch mit einem PermissionDenied Fehler fehl:

db.ref("baskets").on("value", cb)                 // Would fail with PermissionDenied

Sie können auch abfragebasierte Regeln verwenden, um zu begrenzen, wie viele Daten ein Client durch Lesevorgänge herunterlädt.

Die folgende Regel beschränkt beispielsweise den Lesezugriff nur auf die ersten 1000 Ergebnisse einer Abfrage, sortiert nach Priorität:

messages: {
  ".read": "query.orderByKey &&
            query.limitToFirst <= 1000"
}

// Example queries:

db.ref("messages").on("value", cb)                // Would fail with PermissionDenied

db.ref("messages").limitToFirst(1000)
                  .on("value", cb)                // Would succeed (default order by key)

Die folgende query. Ausdrücke sind in den Sicherheitsregeln für Echtzeitdatenbanken verfügbar.

Abfragebasierte Regelausdrücke
Ausdruck Art Beschreibung
query.orderByKey
query.orderByPriority
query.orderByValue
Boolescher Wert True für Abfragen, die nach Schlüssel, Priorität oder Wert sortiert sind. Sonst falsch.
query.orderByChild Zeichenfolge
Null
Verwenden Sie eine Zeichenfolge, um den relativen Pfad zu einem untergeordneten Knoten darzustellen. Beispiel: query.orderByChild == "address/zip" . Wenn die Abfrage nicht von einem untergeordneten Knoten sortiert wird, ist dieser Wert null.
query.startAt
query.endAt
query.equalTo
Zeichenfolge
Nummer
Boolescher Wert
Null
Ruft die Grenzen der ausgeführten Abfrage ab oder gibt null zurück, wenn keine gebundene Menge vorhanden ist.
query.limitToFirst
query.limitToLast
Nummer
Null
Ruft das Limit für die ausgeführte Abfrage ab oder gibt null zurück, wenn kein Limit festgelegt ist.

Nächste Schritte

Nach dieser Erörterung der Bedingungen haben Sie ein besseres Verständnis der Regeln und sind bereit:

Erfahren Sie, wie Sie mit Kernanwendungsfällen umgehen und wie Sie Regeln entwickeln, testen und bereitstellen:

Funktionen für Lernregeln, die für die Echtzeitdatenbank spezifisch sind: