Sprache der Sicherheitsregeln

Firebase-Sicherheitsregeln nutzen flexible, leistungsstarke, benutzerdefinierte Sprachen, die ein breites Spektrum an Komplexität und Granularität unterstützen. Sie können Ihre Regeln so spezifisch oder allgemein gestalten, wie es für Ihre App sinnvoll ist. Echtzeitdatenbankregeln verwenden eine Syntax, die wie JavaScript in einer JSON-Struktur aussieht. Cloud Firestore- und Cloud Storage-Regeln verwenden eine Sprache, die auf der Common Expression Language (CEL) basiert und auf CEL mit match und allow Anweisungen aufbaut, die bedingt gewährten Zugriff unterstützen.

Da es sich jedoch um benutzerdefinierte Sprachen handelt, ist eine Lernkurve erforderlich. Verwenden Sie diesen Leitfaden, um die Regelsprache besser zu verstehen, während Sie tiefer in komplexere Regeln eintauchen.

Wählen Sie ein Produkt aus, um mehr über seine Regeln zu erfahren.

Grundstruktur

Cloud Firestore

Firebase-Sicherheitsregeln in Cloud Firestore und Cloud Storage verwenden die folgende Struktur und Syntax:

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

Beim Erstellen der Regeln ist es wichtig, die folgenden Schlüsselkonzepte zu verstehen:

  • Anfrage: Die Methode oder Methoden, die in der allow Anweisung aufgerufen werden. Dies sind Methoden, deren Ausführung Sie zulassen. Die Standardmethoden sind: get , list , create , update und delete . Die komfortablen read und write ermöglichen einen umfassenden Lese- und Schreibzugriff auf die angegebene Datenbank oder den angegebenen Speicherpfad.
  • Pfad: Die Datenbank oder der Speicherort, dargestellt als URI-Pfad.
  • Regel: Die allow Anweisung, die eine Bedingung enthält, die eine Anfrage zulässt, wenn sie als wahr ausgewertet wird.

Jedes dieser Konzepte wird im Folgenden ausführlicher beschrieben.

Cloud-Speicher

Firebase-Sicherheitsregeln in Cloud Firestore und Cloud Storage verwenden die folgende Struktur und Syntax:

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

Beim Erstellen der Regeln ist es wichtig, die folgenden Schlüsselkonzepte zu verstehen:

  • Anfrage: Die Methode oder Methoden, die in der allow Anweisung aufgerufen werden. Dies sind Methoden, deren Ausführung Sie zulassen. Die Standardmethoden sind: get , list , create , update und delete . Die komfortablen read und write ermöglichen einen umfassenden Lese- und Schreibzugriff auf die angegebene Datenbank oder den angegebenen Speicherpfad.
  • Pfad: Die Datenbank oder der Speicherort, dargestellt als URI-Pfad.
  • Regel: Die allow Anweisung, die eine Bedingung enthält, die eine Anfrage zulässt, wenn sie als wahr ausgewertet wird.

Jedes dieser Konzepte wird im Folgenden ausführlicher beschrieben.

Echtzeitdatenbank

In der Echtzeitdatenbank bestehen Firebase-Sicherheitsregeln aus JavaScript-ähnlichen Ausdrücken, die in einem JSON-Dokument enthalten sind.

Sie verwenden die folgende Syntax:

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

Die Regel besteht aus drei Grundelementen:

  • Pfad: Der Datenbankspeicherort. Dies spiegelt die JSON-Struktur Ihrer Datenbank wider.
  • Anfrage: Dies sind die Methoden, die die Regel verwendet, um Zugriff zu gewähren. Die read und write gewähren umfassenden Lese- und Schreibzugriff, während validate als sekundäre Überprüfung fungieren, um den Zugriff auf der Grundlage eingehender oder vorhandener Daten zu gewähren.
  • Bedingung: Die Bedingung, die eine Anfrage zulässt, wenn sie als wahr ausgewertet wird.

Regelkonstrukte

Cloud Firestore

Die Grundelemente einer Regel in Cloud Firestore und Cloud Storage sind wie folgt:

  • Die service : Deklariert das Firebase-Produkt, für das die Regeln gelten.
  • Der match Block: Definiert einen Pfad in der Datenbank oder im Speicher-Bucket, für den die Regeln gelten.
  • Die allow Anweisung: Stellt Bedingungen für die Gewährung des Zugriffs bereit, differenziert nach Methoden. Zu den unterstützten Methoden gehören: get , list , create , update , delete sowie die praktischen Methoden read und write .
  • Optionale function : Bieten die Möglichkeit, Bedingungen für die Verwendung in mehreren Regeln zu kombinieren und zu umschließen.

Der service enthält einen oder mehrere match Blöcke mit allow -Anweisungen, die Bedingungen bereitstellen, die den Zugriff auf Anforderungen gewähren. Die request und resource stehen zur Verwendung in Regelbedingungen zur Verfügung. Die Firebase Security Rules-Sprache unterstützt auch function .

Syntaxversion

Die syntax gibt die Version der Firebase Rules-Sprache an, die zum Schreiben der Quelle verwendet wird. Die neueste Version der Sprache ist v2 .

rules_version = '2';
service cloud.firestore {
...
}

Wenn keine rules_version Anweisung angegeben wird, werden Ihre Regeln mithilfe der v1 Engine ausgewertet.

Service

Die service definiert, für welches Firebase-Produkt oder welchen Firebase-Dienst Ihre Regeln gelten. Sie können nur eine service pro Quelldatei einschließen.

Cloud Firestore

service cloud.firestore {
 // Your 'match' blocks with their corresponding 'allow' statements and
 // optional 'function' declarations are contained here
}

Cloud-Speicher

service firebase.storage {
  // Your 'match' blocks with their corresponding 'allow' statements and
  // optional 'function' declarations are contained here
}

Wenn Sie mithilfe der Firebase-CLI Regeln für Cloud Firestore und Cloud Storage definieren, müssen Sie diese in separaten Dateien verwalten.

Übereinstimmen

Ein match Block deklariert ein path , das mit dem Pfad für die angeforderte Operation (dem eingehenden request.path ) abgeglichen wird. Der Hauptteil der match muss einen oder mehrere verschachtelte match , allow oder function enthalten. Der Pfad in verschachtelten match Blöcken ist relativ zum Pfad im übergeordneten match Block.

Das path ist ein verzeichnisähnlicher Name, der Variablen oder Platzhalter enthalten kann. Das path ermöglicht Einzelpfad-Segment- und Mehrpfad-Segmentübereinstimmungen. Alle in einem path gebundenen Variablen sind im match oder in jedem verschachtelten Bereich sichtbar, in dem der path deklariert ist.

Übereinstimmungen mit einem path können teilweise oder vollständig sein:

  • Teilweise Übereinstimmungen: Das path ist eine Präfixübereinstimmung mit request.path .
  • Vollständige Übereinstimmungen: Das path stimmt mit dem gesamten request.path überein.

Bei einer vollständigen Übereinstimmung werden die Regeln innerhalb des Blocks ausgewertet. Bei einer teilweisen Übereinstimmung werden die verschachtelten match getestet, um festzustellen, ob ein verschachtelter path die Übereinstimmung abschließt .

Die Regeln in jeder vollständigen match werden ausgewertet, um zu bestimmen, ob die Anforderung zugelassen wird. Wenn eine übereinstimmende Regel Zugriff gewährt, ist die Anfrage zulässig. Wenn keine passende Regel den Zugriff gewährt, wird die Anfrage abgelehnt.

// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
  // Partial match.
  match /example/{singleSegment} {   // `singleSegment` == 'hello'
    allow write;                     // Write rule not evaluated.
    // Complete match.
    match /nested/path {             // `singleSegment` visible in scope.
      allow read;                    // Read rule is evaluated.
    }
  }
  // Complete match.
  match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
    allow read;                      // Read rule is evaluated.
  }
}

Wie das obige Beispiel zeigt, unterstützen die path die folgenden Variablen:

  • Einzelsegment-Platzhalter: Eine Platzhaltervariable wird in einem Pfad deklariert, indem eine Variable in geschweifte Klammern eingeschlossen wird: {variable} . Auf diese Variable kann innerhalb der match Anweisung als string zugegriffen werden.
  • Rekursiver Platzhalter: Der rekursive oder Multisegment-Platzhalter entspricht mehreren Pfadsegmenten auf oder unter einem Pfad. Dieser Platzhalter entspricht allen Pfaden unterhalb des von Ihnen festgelegten Speicherorts. Sie können es deklarieren, indem Sie die Zeichenfolge =** am Ende Ihrer Segmentvariablen hinzufügen: {variable=**} . Auf diese Variable kann innerhalb der match Anweisung als path zugegriffen werden.

Erlauben

Der match Block enthält eine oder mehrere allow Anweisungen. Das sind Ihre eigentlichen Regeln. Sie können allow auf eine oder mehrere Methoden anwenden. Die Bedingungen einer allow müssen als „wahr“ ausgewertet werden, damit Cloud Firestore oder Cloud Storage eingehende Anfragen gewähren kann. Sie können allow Anweisungen auch ohne Bedingungen schreiben, beispielsweise allow read . Wenn die allow Anweisung jedoch keine Bedingung enthält, lässt sie die Anforderung für diese Methode immer zu.

Wenn eine der allow für die Methode erfüllt ist, wird die Anforderung zugelassen. Wenn darüber hinaus eine umfassendere Regel den Zugriff gewährt, gewähren die Regeln den Zugriff und ignorieren alle detaillierteren Regeln, die den Zugriff einschränken könnten.

Betrachten Sie das folgende Beispiel, in dem jeder Benutzer jede seiner eigenen Dateien lesen oder löschen kann. Eine detailliertere Regel erlaubt Schreibvorgänge nur, wenn der Benutzer, der den Schreibvorgang anfordert, Eigentümer der Datei ist und es sich bei der Datei um eine PNG-Datei handelt. Ein Benutzer kann alle Dateien im Unterpfad löschen – auch wenn es sich nicht um PNGs handelt –, da die frühere Regel dies zulässt.

service firebase.storage {
  // Allow the requestor to read or delete any resource on a path under the
  // user directory.
  match /users/{userId}/{anyUserFile=**} {
    allow read, delete: if request.auth != null && request.auth.uid == userId;
  }

  // Allow the requestor to create or update their own images.
  // When 'request.method' == 'delete' this rule and the one matching
  // any path under the user directory would both match and the `delete`
  // would be permitted.

  match /users/{userId}/images/{imageId} {
    // Whether to permit the request depends on the logical OR of all
    // matched rules. This means that even if this rule did not explicitly
    // allow the 'delete' the earlier rule would have.
    allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
  }
}

Methode

Jede allow Anweisung enthält eine Methode, die eingehenden Anforderungen derselben Methode Zugriff gewährt.

Methode Art der Anfrage
Convenience-Methoden
read Jede Art von Leseanfrage
write Jede Art von Schreibanfrage
Standardmethoden
get Leseanfragen für einzelne Dokumente oder Dateien
list Lesen Sie Anfragen für Anfragen und Inkasso
create Schreiben Sie neue Dokumente oder Dateien
update Schreiben Sie in vorhandene Datenbankdokumente oder aktualisieren Sie Dateimetadaten
delete Daten löschen

Sie können keine Lesemethoden im selben match Block oder widersprüchliche Schreibmethoden in derselben path überlappen.

Beispielsweise würden die folgenden Regeln fehlschlagen:

service bad.example {
  match /rules/with/overlapping/methods {
    // This rule allows reads to all authenticated users
    allow read: if request.auth != null;

    match another/subpath {
      // This secondary, more specific read rule causes an error
      allow get: if request.auth != null && request.auth.uid == "me";
      // Overlapping write methods in the same path cause an error as well
      allow write: if request.auth != null;
      allow create: if request.auth != null && request.auth.uid == "me";
    }
  }
}

Funktion

Wenn Ihre Sicherheitsregeln komplexer werden, möchten Sie möglicherweise Bedingungssätze in Funktionen einbinden, die Sie in Ihrem Regelsatz wiederverwenden können. Sicherheitsregeln unterstützen benutzerdefinierte Funktionen. Die Syntax für benutzerdefinierte Funktionen ähnelt ein wenig der von JavaScript, Sicherheitsregelfunktionen sind jedoch in einer domänenspezifischen Sprache geschrieben, die einige wichtige Einschränkungen aufweist:

  • Funktionen können nur eine einzige return Anweisung enthalten. Sie dürfen keine zusätzliche Logik enthalten. Sie können beispielsweise keine Schleifen ausführen oder externe Dienste aufrufen.
  • Funktionen können automatisch auf Funktionen und Variablen aus dem Bereich zugreifen, in dem sie definiert sind. Beispielsweise hat eine im service cloud.firestore definierte Funktion Zugriff auf die resource und integrierte Funktionen wie get() und exists() .
  • Funktionen können andere Funktionen aufrufen, dürfen jedoch nicht rekursiv sein. Die Gesamttiefe des Aufrufstapels ist auf 20 begrenzt.
  • In der Regelversion v2 können Funktionen Variablen mithilfe des Schlüsselworts let definieren. Funktionen können bis zu 10 Let-Bindungen haben, müssen jedoch mit einer Return-Anweisung enden.

Eine Funktion wird mit dem Schlüsselwort function definiert und akzeptiert null oder mehr Argumente. Beispielsweise möchten Sie möglicherweise die beiden in den obigen Beispielen verwendeten Arten von Bedingungen in einer einzigen Funktion kombinieren:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

Hier ist ein Beispiel, das Funktionsargumente und Let-Zuweisungen zeigt. Zuweisungsanweisungen müssen durch Semikolons getrennt werden.

function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
  return isAuthor || isAdmin;
}

Beachten Sie, wie die isAdmin Zuweisung eine Suche in der Admins-Sammlung erzwingt. Für eine verzögerte Auswertung ohne unnötige Suchvorgänge nutzen Sie die Kurzschlussfunktion von && (AND) und || (OR)-Vergleiche, um eine zweite Funktion nur dann aufzurufen, wenn isAuthor als wahr (für && Vergleiche) oder falsch (für || -Vergleiche) angezeigt wird.

function isAdmin(userId) {
  return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  // `||` is short-circuiting; isAdmin called only if isAuthor == false.
  return isAuthor || isAdmin(userId);
}

Durch die Verwendung von Funktionen in Ihren Sicherheitsregeln werden diese bei zunehmender Komplexität Ihrer Regeln besser wartbar.

Cloud-Speicher

Die Grundelemente einer Regel in Cloud Firestore und Cloud Storage sind wie folgt:

  • Die service : Deklariert das Firebase-Produkt, für das die Regeln gelten.
  • Der match Block: Definiert einen Pfad in der Datenbank oder im Speicher-Bucket, für den die Regeln gelten.
  • Die allow Anweisung: Stellt Bedingungen für die Gewährung des Zugriffs bereit, differenziert nach Methoden. Zu den unterstützten Methoden gehören: get , list , create , update , delete sowie die praktischen Methoden read und write .
  • Optionale function : Bieten die Möglichkeit, Bedingungen für die Verwendung in mehreren Regeln zu kombinieren und zu umschließen.

Der service enthält einen oder mehrere match Blöcke mit allow -Anweisungen, die Bedingungen bereitstellen, die den Zugriff auf Anforderungen gewähren. Die request und resource stehen zur Verwendung in Regelbedingungen zur Verfügung. Die Firebase Security Rules-Sprache unterstützt auch function .

Syntaxversion

Die syntax gibt die Version der Firebase-Regelsprache an, die zum Schreiben der Quelle verwendet wurde. Die neueste Version der Sprache ist v2 .

rules_version = '2';
service cloud.firestore {
...
}

Wenn keine rules_version Anweisung angegeben wird, werden Ihre Regeln mithilfe der v1 Engine ausgewertet.

Service

Die service definiert, für welches Firebase-Produkt oder welchen Firebase-Dienst Ihre Regeln gelten. Sie können nur eine service pro Quelldatei einschließen.

Cloud Firestore

service cloud.firestore {
 // Your 'match' blocks with their corresponding 'allow' statements and
 // optional 'function' declarations are contained here
}

Cloud-Speicher

service firebase.storage {
  // Your 'match' blocks with their corresponding 'allow' statements and
  // optional 'function' declarations are contained here
}

Wenn Sie mithilfe der Firebase-CLI Regeln für Cloud Firestore und Cloud Storage definieren, müssen Sie diese in separaten Dateien verwalten.

Übereinstimmen

Ein match Block deklariert ein path , das mit dem Pfad für die angeforderte Operation (dem eingehenden request.path ) abgeglichen wird. Der Hauptteil der match muss einen oder mehrere verschachtelte match , allow oder function enthalten. Der Pfad in verschachtelten match Blöcken ist relativ zum Pfad im übergeordneten match Block.

Das path ist ein verzeichnisähnlicher Name, der Variablen oder Platzhalter enthalten kann. Das path ermöglicht Einzelpfad-Segment- und Mehrpfad-Segmentübereinstimmungen. Alle in einem path gebundenen Variablen sind im match oder in jedem verschachtelten Bereich sichtbar, in dem der path deklariert ist.

Übereinstimmungen mit einem path können teilweise oder vollständig sein:

  • Teilweise Übereinstimmungen: Das path ist eine Präfixübereinstimmung mit request.path .
  • Vollständige Übereinstimmungen: Das path stimmt mit dem gesamten request.path überein.

Bei einer vollständigen Übereinstimmung werden die Regeln innerhalb des Blocks ausgewertet. Bei einer teilweisen Übereinstimmung werden die verschachtelten match getestet, um festzustellen, ob ein verschachtelter path die Übereinstimmung abschließt .

Die Regeln in jeder vollständigen match werden ausgewertet, um zu bestimmen, ob die Anforderung zugelassen wird. Wenn eine übereinstimmende Regel Zugriff gewährt, ist die Anfrage zulässig. Wenn keine passende Regel den Zugriff gewährt, wird die Anfrage abgelehnt.

// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
  // Partial match.
  match /example/{singleSegment} {   // `singleSegment` == 'hello'
    allow write;                     // Write rule not evaluated.
    // Complete match.
    match /nested/path {             // `singleSegment` visible in scope.
      allow read;                    // Read rule is evaluated.
    }
  }
  // Complete match.
  match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
    allow read;                      // Read rule is evaluated.
  }
}

Wie das obige Beispiel zeigt, unterstützen die path die folgenden Variablen:

  • Einzelsegment-Platzhalter: Eine Platzhaltervariable wird in einem Pfad deklariert, indem eine Variable in geschweifte Klammern eingeschlossen wird: {variable} . Auf diese Variable kann innerhalb der match Anweisung als string zugegriffen werden.
  • Rekursiver Platzhalter: Der rekursive oder Multisegment-Platzhalter entspricht mehreren Pfadsegmenten auf oder unter einem Pfad. Dieser Platzhalter entspricht allen Pfaden unterhalb des von Ihnen festgelegten Speicherorts. Sie können es deklarieren, indem Sie die Zeichenfolge =** am Ende Ihrer Segmentvariablen hinzufügen: {variable=**} . Auf diese Variable kann innerhalb der match Anweisung als path zugegriffen werden.

Erlauben

Der match Block enthält eine oder mehrere allow Anweisungen. Das sind Ihre eigentlichen Regeln. Sie können allow auf eine oder mehrere Methoden anwenden. Die Bedingungen einer allow müssen als „wahr“ ausgewertet werden, damit Cloud Firestore oder Cloud Storage eingehende Anfragen gewähren kann. Sie können allow Anweisungen auch ohne Bedingungen schreiben, beispielsweise allow read . Wenn die allow Anweisung jedoch keine Bedingung enthält, lässt sie die Anforderung für diese Methode immer zu.

Wenn eine der allow für die Methode erfüllt ist, wird die Anforderung zugelassen. Wenn darüber hinaus eine umfassendere Regel den Zugriff gewährt, gewähren die Regeln den Zugriff und ignorieren alle detaillierteren Regeln, die den Zugriff einschränken könnten.

Betrachten Sie das folgende Beispiel, in dem jeder Benutzer jede seiner eigenen Dateien lesen oder löschen kann. Eine detailliertere Regel erlaubt Schreibvorgänge nur, wenn der Benutzer, der den Schreibvorgang anfordert, Eigentümer der Datei ist und es sich bei der Datei um eine PNG-Datei handelt. Ein Benutzer kann alle Dateien im Unterpfad löschen – auch wenn es sich nicht um PNGs handelt –, da die frühere Regel dies zulässt.

service firebase.storage {
  // Allow the requestor to read or delete any resource on a path under the
  // user directory.
  match /users/{userId}/{anyUserFile=**} {
    allow read, delete: if request.auth != null && request.auth.uid == userId;
  }

  // Allow the requestor to create or update their own images.
  // When 'request.method' == 'delete' this rule and the one matching
  // any path under the user directory would both match and the `delete`
  // would be permitted.

  match /users/{userId}/images/{imageId} {
    // Whether to permit the request depends on the logical OR of all
    // matched rules. This means that even if this rule did not explicitly
    // allow the 'delete' the earlier rule would have.
    allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
  }
}

Methode

Jede allow Anweisung enthält eine Methode, die eingehenden Anforderungen derselben Methode Zugriff gewährt.

Methode Art der Anfrage
Convenience-Methoden
read Jede Art von Leseanfrage
write Jede Art von Schreibanfrage
Standardmethoden
get Leseanfragen für einzelne Dokumente oder Dateien
list Lesen Sie Anfragen für Anfragen und Inkasso
create Schreiben Sie neue Dokumente oder Dateien
update Schreiben Sie in vorhandene Datenbankdokumente oder aktualisieren Sie Dateimetadaten
delete Daten löschen

Sie können keine Lesemethoden im selben match Block oder widersprüchliche Schreibmethoden in derselben path überlappen.

Beispielsweise würden die folgenden Regeln fehlschlagen:

service bad.example {
  match /rules/with/overlapping/methods {
    // This rule allows reads to all authenticated users
    allow read: if request.auth != null;

    match another/subpath {
      // This secondary, more specific read rule causes an error
      allow get: if request.auth != null && request.auth.uid == "me";
      // Overlapping write methods in the same path cause an error as well
      allow write: if request.auth != null;
      allow create: if request.auth != null && request.auth.uid == "me";
    }
  }
}

Funktion

Wenn Ihre Sicherheitsregeln komplexer werden, möchten Sie möglicherweise Bedingungssätze in Funktionen einbinden, die Sie in Ihrem Regelsatz wiederverwenden können. Sicherheitsregeln unterstützen benutzerdefinierte Funktionen. Die Syntax für benutzerdefinierte Funktionen ähnelt ein wenig der von JavaScript, Sicherheitsregelfunktionen sind jedoch in einer domänenspezifischen Sprache geschrieben, die einige wichtige Einschränkungen aufweist:

  • Funktionen können nur eine einzige return Anweisung enthalten. Sie dürfen keine zusätzliche Logik enthalten. Sie können beispielsweise keine Schleifen ausführen oder externe Dienste aufrufen.
  • Funktionen können automatisch auf Funktionen und Variablen aus dem Bereich zugreifen, in dem sie definiert sind. Beispielsweise hat eine im service cloud.firestore definierte Funktion Zugriff auf die resource und integrierte Funktionen wie get() und exists() .
  • Funktionen können andere Funktionen aufrufen, dürfen jedoch nicht rekursiv sein. Die Gesamttiefe des Aufrufstapels ist auf 20 begrenzt.
  • In der Regelversion v2 können Funktionen Variablen mithilfe des Schlüsselworts let definieren. Funktionen können bis zu 10 Let-Bindungen haben, müssen jedoch mit einer Return-Anweisung enden.

Eine Funktion wird mit dem Schlüsselwort function definiert und akzeptiert null oder mehr Argumente. Beispielsweise möchten Sie möglicherweise die beiden in den obigen Beispielen verwendeten Arten von Bedingungen in einer einzigen Funktion kombinieren:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

Hier ist ein Beispiel, das Funktionsargumente und Let-Zuweisungen zeigt. Zuweisungsanweisungen müssen durch Semikolons getrennt werden.

function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
  return isAuthor || isAdmin;
}

Beachten Sie, wie die isAdmin Zuweisung eine Suche in der Admins-Sammlung erzwingt. Für eine verzögerte Auswertung ohne unnötige Suchvorgänge nutzen Sie die Kurzschlussfunktion von && (AND) und || (OR)-Vergleiche, um eine zweite Funktion nur aufzurufen, wenn isAuthor als wahr (für && Vergleiche) oder falsch (für || -Vergleiche) angezeigt wird.

function isAdmin(userId) {
  return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  // `||` is short-circuiting; isAdmin called only if isAuthor == false.
  return isAuthor || isAdmin(userId);
}

Durch die Verwendung von Funktionen in Ihren Sicherheitsregeln werden diese bei zunehmender Komplexität Ihrer Regeln besser wartbar.

Echtzeitdatenbank

Wie oben beschrieben umfassen Echtzeitdatenbankregeln drei Grundelemente: den Datenbankspeicherort als Spiegel der JSON-Struktur der Datenbank, den Anforderungstyp und die Bedingung, die den Zugriff gewährt.

Datenbankspeicherort

Die Struktur Ihrer Regeln sollte der Struktur der Daten folgen, die Sie in Ihrer Datenbank gespeichert haben. In einer Chat-App mit einer Liste von Nachrichten könnten beispielsweise Daten vorliegen, die wie folgt aussehen:

  {
    "messages": {
      "message0": {
        "content": "Hello",
        "timestamp": 1405704370369
      },
      "message1": {
        "content": "Goodbye",
        "timestamp": 1405704395231
      },
      ...
    }
  }

Ihre Regeln sollten diese Struktur widerspiegeln. Zum Beispiel:

  {
    "rules": {
      "messages": {
        "$message": {
          // only messages from the last ten minutes can be read
          ".read": "data.child('timestamp').val() > (now - 600000)",

          // new messages must have a string content and a number timestamp
          ".validate": "newData.hasChildren(['content', 'timestamp']) &&
                        newData.child('content').isString() &&
                        newData.child('timestamp').isNumber()"
        }
      }
    }
  }

Wie das obige Beispiel zeigt, unterstützen Echtzeitdatenbankregeln eine $location -Variable, um Pfadsegmente abzugleichen. Verwenden Sie das Präfix $ vor Ihrem Pfadsegment, um Ihre Regel an alle untergeordneten Knoten entlang des Pfads anzupassen.

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

Sie können die $variable auch parallel zu konstanten Pfadnamen verwenden.

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

Methode

In der Echtzeitdatenbank gibt es drei Arten von Regeln. Zwei dieser Regeltypen – read und write – gelten für die Methode einer eingehenden Anfrage. Der validate erzwingt Datenstrukturen und validiert das Format und den Inhalt der Daten. Regeln führen .validate Regeln aus, nachdem sie überprüft haben, ob eine .write Regel Zugriff gewährt.

Regeltypen
.lesen Beschreibt, ob und wann Daten von Benutzern gelesen werden dürfen.
.schreiben Beschreibt, ob und wann Daten geschrieben werden dürfen.
.bestätigen Definiert, wie ein korrekt formatierter Wert aussieht, ob er über untergeordnete Attribute verfügt und welchen Datentyp er hat.

Wenn es keine Regel gibt, die dies zulässt, wird der Zugriff auf einen Pfad standardmäßig verweigert.

Baubedingungen

Cloud Firestore

Eine Bedingung ist ein boolescher Ausdruck, der bestimmt, ob ein bestimmter Vorgang zugelassen oder verweigert werden soll. Die request und resource stellen den Kontext für diese Bedingungen bereit.

Die request

Die request umfasst die folgenden Felder und entsprechende Informationen:

request.auth

Ein JSON-Web-Token (JWT), das Authentifizierungsdaten von Firebase Authentication enthält. Das auth enthält eine Reihe von Standardansprüchen und alle benutzerdefinierten Ansprüche, die Sie über die Firebase-Authentifizierung erstellen. Erfahren Sie mehr über Firebase-Sicherheitsregeln und -Authentifizierung .

request.method

Bei der request.method kann es sich um eine beliebige Standardmethode oder eine benutzerdefinierte Methode handeln. Zur Vereinfachung des Schreibens gibt es auch die Komfortmethoden read und write , die für alle schreibgeschützten bzw. alle schreibgeschützten Standardmethoden gelten.

request.params

Die request.params enthalten alle Daten, die sich nicht speziell auf die request.resource beziehen und für die Auswertung nützlich sein könnten. In der Praxis sollte diese Karte für alle Standardmethoden leer sein und für benutzerdefinierte Methoden Nicht-Ressourcendaten enthalten. Dienste müssen darauf achten, die als Parameter dargestellten Schlüssel und Werte nicht umzubenennen oder deren Typ zu ändern.

request.path

Der request.path ist der Pfad für die resource . Der Pfad ist relativ zum Dienst. Pfadsegmente, die nicht URL-sichere Zeichen wie / enthalten, sind URL-codiert.

Die resource

Die resource ist der aktuelle Wert innerhalb des Dienstes, der als Karte von Schlüssel-Wert-Paaren dargestellt wird. Das Verweisen auf eine resource innerhalb einer Bedingung führt dazu, dass der Wert höchstens einmal vom Dienst gelesen wird. Diese Suche wird auf alle dienstbezogenen Kontingente für die Ressource angerechnet. Bei get Anfragen wird die resource nur bei Ablehnung auf das Kontingent angerechnet.

Operatoren und Operatorpriorität

Verwenden Sie die folgende Tabelle als Referenz für Operatoren und ihre entsprechende Rangfolge in Regeln für Cloud Firestore und Cloud Storage.

Gegeben seien beliebige Ausdrücke a und b , ein Feld f und ein Index i .

Operator Beschreibung Assoziativität
a[i] a() af Index, Aufruf, Feldzugriff links nach rechts
!a -a Unäre Negation rechts nach links
a/ba%ba*b Multiplikative Operatoren links nach rechts
a+b ab Additive Operatoren links nach rechts
a>ba>=ba Vergleichsoperatoren links nach rechts
a in b Existenz in Liste oder Karte links nach rechts
a is type Typvergleich, wobei type bool, int, float, Zahl, String, Liste, Karte, Zeitstempel, Dauer, Pfad oder Breite sein kann links nach rechts
a==ba!=b Vergleichsoperatoren links nach rechts
a && b Bedingtes UND links nach rechts
a || b Bedingtes ODER links nach rechts
a ? true_value : false_value Ternärer Ausdruck links nach rechts

Cloud-Speicher

Eine Bedingung ist ein boolescher Ausdruck, der bestimmt, ob ein bestimmter Vorgang zugelassen oder verweigert werden soll. Die request und resource stellen den Kontext für diese Bedingungen bereit.

Die request

Die request umfasst die folgenden Felder und entsprechende Informationen:

request.auth

Ein JSON-Web-Token (JWT), das Authentifizierungsdaten von Firebase Authentication enthält. Das auth enthält eine Reihe von Standardansprüchen und alle benutzerdefinierten Ansprüche, die Sie über die Firebase-Authentifizierung erstellen. Erfahren Sie mehr über Firebase-Sicherheitsregeln und -Authentifizierung .

request.method

Bei der request.method kann es sich um eine beliebige Standardmethode oder eine benutzerdefinierte Methode handeln. Zur Vereinfachung des Schreibens gibt es auch die Komfortmethoden read und write , die für alle schreibgeschützten bzw. alle schreibgeschützten Standardmethoden gelten.

request.params

Die request.params enthalten alle Daten, die sich nicht speziell auf die request.resource beziehen und für die Auswertung nützlich sein könnten. In der Praxis sollte diese Karte für alle Standardmethoden leer sein und für benutzerdefinierte Methoden Nicht-Ressourcendaten enthalten. Dienste müssen darauf achten, die als Parameter dargestellten Schlüssel und Werte nicht umzubenennen oder deren Typ zu ändern.

request.path

Der request.path ist der Pfad für die resource . Der Pfad ist relativ zum Dienst. Pfadsegmente, die nicht URL-sichere Zeichen wie / enthalten, sind URL-codiert.

Die resource

Die resource ist der aktuelle Wert innerhalb des Dienstes, der als Karte von Schlüssel-Wert-Paaren dargestellt wird. Das Verweisen auf eine resource innerhalb einer Bedingung führt dazu, dass der Wert höchstens einmal vom Dienst gelesen wird. Diese Suche wird auf alle dienstbezogenen Kontingente für die Ressource angerechnet. Bei get Anfragen wird die resource nur bei Ablehnung auf das Kontingent angerechnet.

Operatoren und Operatorpriorität

Verwenden Sie die folgende Tabelle als Referenz für Operatoren und ihre entsprechende Rangfolge in Regeln für Cloud Firestore und Cloud Storage.

Gegeben seien beliebige Ausdrücke a und b , ein Feld f und ein Index i .

Operator Beschreibung Assoziativität
a[i] a() af Index, Aufruf, Feldzugriff links nach rechts
!a -a Unäre Negation rechts nach links
a/ba%ba*b Multiplikative Operatoren links nach rechts
a+b ab Additive Operatoren links nach rechts
a>ba>=ba Vergleichsoperatoren links nach rechts
a in b Existenz in Liste oder Karte links nach rechts
a is type Typvergleich, wobei type bool, int, float, Zahl, String, Liste, Karte, Zeitstempel, Dauer, Pfad oder Breite sein kann links nach rechts
a==ba!=b Vergleichsoperatoren links nach rechts
a && b Bedingtes UND links nach rechts
a || b Bedingtes ODER links nach rechts
a ? true_value : false_value Ternärer Ausdruck links nach rechts

Echtzeitdatenbank

Eine Bedingung ist ein boolescher Ausdruck, der bestimmt, ob ein bestimmter Vorgang zugelassen oder verweigert werden soll. Sie können diese Bedingungen in Realtime Database Rules auf folgende Weise definieren.

Vordefinierte Variablen

Es gibt eine Reihe hilfreicher, vordefinierter Variablen, auf die innerhalb einer Regeldefinition zugegriffen werden kann. Hier ist jeweils eine kurze Zusammenfassung:

Vordefinierte Variablen
Jetzt Die aktuelle Zeit in Millisekunden seit der Linux-Epoche. Dies eignet sich besonders gut für die Validierung von Zeitstempeln, die mit firebase.database.ServerValue.TIMESTAMP des SDK erstellt wurden.
Wurzel Ein RuleDataSnapshot , der den Stammpfad in der Firebase-Datenbank darstellt, wie er vor dem versuchten Vorgang vorhanden war.
neue Daten Ein RuleDataSnapshot , der die Daten darstellt, wie sie nach dem versuchten Vorgang vorhanden wären. Es umfasst die neuen Daten, die geschrieben werden, und die vorhandenen Daten.
Daten Ein RuleDataSnapshot, der die Daten darstellt, wie sie vor dem versuchten Vorgang vorhanden waren.
$-Variablen Ein Platzhalterpfad zur Darstellung von IDs und dynamischen untergeordneten Schlüsseln.
Autor Stellt die Token-Nutzlast eines authentifizierten Benutzers dar.

Diese Variablen können überall in Ihren Regeln verwendet werden. Die folgenden Sicherheitsregeln stellen beispielsweise sicher, dass die auf den /foo/ -Knoten geschriebenen Daten eine Zeichenfolge mit weniger als 100 Zeichen sein müssen:

{
  "rules": {
    "foo": {
      // /foo is readable by the world
      ".read": true,

      // /foo is writable by the world
      ".write": true,

      // data written to /foo must be a string less than 100 characters
      ".validate": "newData.isString() && newData.val().length < 100"
    }
  }
}

Datenbasierte Regeln

Alle Daten in Ihrer Datenbank können in Ihren Regeln verwendet werden. Mithilfe der vordefinierten Variablen root , data und newData können Sie auf jeden Pfad zugreifen, wie er 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 ist, für den übergeordneten Knoten kein readOnly Flag gesetzt ist und in den neu geschriebenen Daten ein untergeordneter Knoten namens foo vorhanden ist:

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

Abfragebasierte Regeln

Obwohl Sie Regeln nicht als Filter verwenden können, können Sie den Zugriff auf Teilmengen von Daten beschränken, indem Sie Abfrageparameter in Ihren Regeln verwenden. 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, deren Eigentümer der aktive Benutzer ist:

"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 würden Abfragen, die die Parameter in der Regel nicht enthalten, mit einem PermissionDenied Fehler fehlschlagen:

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, geordnet 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 Echtzeit-Datenbanksicherheitsregeln verfügbar.

Abfragebasierte Regelausdrücke
Ausdruck Typ 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 nach einem untergeordneten Knoten sortiert ist, ist dieser Wert null.
query.startAt
query.endAt
query.equalTo
Zeichenfolge
Nummer
Boolescher Wert
Null
Ruft die Grenzen der ausführenden Abfrage ab oder gibt null zurück, wenn kein gebundener Satz vorhanden ist.
query.limitToFirst
query.limitToLast
Nummer
Null
Ruft den Grenzwert für die ausgeführte Abfrage ab oder gibt null zurück, wenn kein Grenzwert festgelegt ist.

Betreiber

Echtzeitdatenbankregeln unterstützen eine Reihe von Operatoren, mit denen Sie Variablen in der Bedingungsanweisung kombinieren können. Die vollständige Liste der Operatoren finden Sie in der Referenzdokumentation .

Bedingungen schaffen

Ihre tatsächlichen Bedingungen variieren je nach dem Zugriff, den Sie gewähren möchten. Regeln bieten bewusst ein enormes Maß an Flexibilität, sodass die Regeln Ihrer App letztlich so einfach oder so komplex sein können, wie Sie es benötigen.

Hinweise zum Erstellen einfacher, produktionsbereiter Regeln finden Sie unter Grundlegende Sicherheitsregeln .