Nutzungsbedingungen in Echtzeit-Datenbankregeln

Dieser Leitfaden baut auf den den Kern Firebase Sicherheitsregeln Sprache lernen Führer zu zeigen , wie Bedingungen Sicherheitsregeln zu Ihrer Firebase Realtime - Datenbank hinzuzufügen.

Der primäre Baustein der Realtime - Datenbank Sicherheitsregeln ist die Bedingung. Eine Bedingung ist ein boolescher Ausdruck, der bestimmt, ob eine bestimmte Operation zugelassen oder abgelehnt werden soll. Für Grundregeln, mit true und false arbeitet Literale als Bedingungen prefectly gut. Aber die Sprache der Echtzeit-Datenbanksicherheitsregeln bietet Ihnen Möglichkeiten, komplexere Bedingungen zu schreiben, die:

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

Verwenden von $-Variablen zum Erfassen von Pfadsegmenten

Sie können mit der , indem er erklärt Capture - Variablen für einen Lese- oder Schreib Teile des Pfades erfassen $ prefix. 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 mit konstanten Pfadnamen verwendet werden. In diesem Beispiel verwenden wir den $other Variable eine erklären .validate Regel , die sicherstellt , dass widget keine Kinder außer hat title und color . Jeder Schreibvorgang, der dazu führen würde, dass zusätzliche untergeordnete Elemente erstellt werden, würde fehlschlagen.

{
  "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 gängigsten Sicherheitsregelmuster ist die Zugriffssteuerung basierend auf dem Authentifizierungsstatus des Benutzers. Ihre App möchte beispielsweise nur angemeldeten Benutzern erlauben, Daten zu schreiben.

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

Firebase Authentication ist in die Firebase Realtime Database integriert, damit Sie den Datenzugriff anhand von Bedingungen pro Benutzer 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. Diese Informationen umfassen die eindeutige Kennung ( uid ) sowie verknüpfte Kontodaten, wie zum Beispiel einer 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 Security Rules 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 authentifiziert Firebase - Authentifizierung wird es die folgenden Attribute enthalten:

Anbieter Die verwendete Authentifizierungsmethode („Passwort“, „anonym“, „facebook“, „github“, „google“ oder „twitter“).
uid Eine eindeutige Benutzer-ID, die garantiert über alle Anbieter hinweg eindeutig ist.
Zeichen Der Inhalt des Firebase-Authentifizierungs-ID-Tokens. Siehe die Referenzdokumentation für auth.token für weitere Details.

Hier ist ein Beispiel Regel, die verwendet auth Variable , um sicherzustellen , dass jeder Benutzer nur auf einen benutzerspezifischen Pfad schreiben:

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

Strukturieren Ihrer Datenbank, um Authentifizierungsbedingungen zu unterstützen

Normalerweise ist es hilfreich, Ihre Datenbank so zu strukturieren, dass das Schreiben von Regeln einfacher wird. Ein gemeinsames Muster von Benutzerdaten in der Echtzeit - Datenbank zur Speicherung ist alle Benutzer zu speichern , in einem einzigen users Knoten , deren Kinder die uid - Werte für jeden Benutzer. Wenn Sie den Zugriff auf diese Daten so einschränken möchten, dass nur der eingeloggte Benutzer seine eigenen Daten sehen kann, würden Ihre Regeln in etwa so aussehen.

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

Arbeiten mit benutzerdefinierten Authentifizierungsansprüchen

Für Anwendungen , die individuelle Zugriffskontrolle für verschiedene Benutzer erfordern, ermöglicht Firebase Authentication Entwickler Set Ansprüche auf Firebase Benutzer . Diese Ansprüche sind zugänglich in der auth.token Variable in Ihren Regeln. Hier ist ein Beispiel für Regeln, die von dem machen hasEmergencyTowel benutzerdefinierten Anspruch:

{
  "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 Authentifizierungstoken können optional Ansprüche auf diese Tokens hinzu. Diese Behauptungen sind avaialble auf der auth.token Variable in Ihren Regeln.

Bestehende Daten vs. neue Daten

Die vordefinierte data wird verwendet , um die Daten vor einer Schreiboperation beziehen erfolgt. Umgekehrt ist die newData enthält Variable , um die neuen Daten , die , wenn der Schreibvorgang erfolgreich bestehen werden. newData stellt das fusionierte Ergebnis werden die neuen Daten geschrieben werden und die bestehenden 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 bestehenden Daten vorzunehmen, die nicht null sind:

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

Als Kriterium für Regeln können beliebige Daten verwendet werden. Mit Hilfe der vordefinierten Variablen root , data und newData , können wir jeden Pfad zugreifen , wie es vor oder nach einem Schreibereignis existieren würde.

Betrachten Sie dieses Beispiel, die Schreiboperationen , solange der Wert des erlaubt /allow_writes/ Knoten ist true , der übergeordnete Knoten ein nicht haben readOnly - Flag gesetzt, und es gibt ein Kind namens foo in den neu geschriebenen Daten:

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

Daten validieren

Erzwingen von Datenstrukturen und die Validierung des Format und den Inhalt der Daten sollte unter Verwendung erfolgen .validate Regeln, die nur ausgeführt werden , nachdem ein .write Regel Zugang zu gewähren , erfolgreich ist . Unten ist eine Beispiel .validate Regeldefinition , die nur Datumsangaben im Format 1900-2099 YYYY-MM-DD zwischen den Jahren ermöglicht, die einen regulären Ausdruck geprü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 einzige Art von Sicherheitsregel , die nicht Kaskade tun. Wenn eine Validierungsregel für einen untergeordneten Datensatz fehlschlägt, wird der gesamte Schreibvorgang abgelehnt. Darüber hinaus werden die Validate Definitionen ignoriert , wenn Daten gelöscht werden (das heißt, wenn der neue Wert geschrieben wird null ).

Dies mag trivial erscheinen, ist aber in der Tat wichtige Funktionen für das Schreiben leistungsstarker Firebase Realtime Database Security Rules. 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()"
      }
    }
  }
}

Betrachten Sie für diese Variante die Ergebnisse der folgenden Schreiboperationen:

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
Hinweis: Dieses Produkt ist Firebase im App Clip Ziel nicht verfügbar ist .
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
Hinweis: Dieses Produkt ist Firebase im App Clip Ziel nicht verfügbar ist .
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

Jetzt einen Blick auf die gleiche Struktur lassen, aber mit .write Regeln statt .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 jede 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
Hinweis: Dieses Produkt ist Firebase im App Clip Ziel nicht verfügbar ist .
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
Hinweis: Dieses Produkt ist Firebase im App Clip Ziel nicht verfügbar ist .
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 veranschaulicht die Unterschiede zwischen .write und .validate Regeln. Wie gezeigt, sollte alle diese Regeln geschrieben werden mit .validate , mit der möglichen Ausnahme der newData.hasChildren() Regel, die , ob Löschungen sollten erlaubt sein abhängen würde.

Abfragebasierte Regeln

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

Zum Beispiel verwendet die folgende abfragebasierte Regel benutzerbasierte Sicherheitsregeln und abfragebasierte Regeln Zugriff auf die Daten in der beschränken baskets Sammlung nur auf die Einkaufskörbe 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

Allerdings Abfragen , die mit einem nicht über die Parameter in der Regel umfassen würde scheitern PermissionDenied Fehler:

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 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 Typ Beschreibung
query.orderByKey
query.orderByPriority
query.orderByValue
boolesch True für Abfragen, die nach Schlüssel, Priorität oder Wert geordnet sind. Sonst falsch.
query.orderByChild Schnur
Null
Verwenden Sie eine Zeichenfolge, um den relativen Pfad zu einem untergeordneten Knoten darzustellen. Zum Beispiel query.orderByChild == "address/zip" . Wenn die Abfrage nicht nach einem untergeordneten Knoten sortiert ist, ist dieser Wert null.
query.startAt
query.endAt
query.equalTo
Schnur
Nummer
boolesch
Null
Ruft die Grenzen der ausgeführten Abfrage ab oder gibt null zurück, wenn kein gebundener Satz 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 Diskussion der Bedingungen haben Sie ein besseres Verständnis von Regeln und sind bereit:

Erfahren Sie, wie Sie mit Kernanwendungsfällen umgehen, und lernen Sie den Workflow zum Entwickeln, Testen und Bereitstellen von Regeln kennen:

Learn Rules-Funktionen, die spezifisch für Realtime Database sind: