Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Lernen Sie die Kernsyntax der Sprache der Echtzeit-Datenbankregeln

Mit den Sicherheitsregeln für Firebase-Echtzeitdatenbanken können Sie den Zugriff auf in Ihrer Datenbank gespeicherte Daten steuern. Mit der flexiblen Regelsyntax können Sie Regeln erstellen, die mit allen Übereinstimmungen übereinstimmen, von allen Schreibvorgängen in Ihre Datenbank bis hin zu Operationen auf einzelnen Knoten.

Echtzeit-Datenbanksicherheitsregeln sind deklarative Konfigurationen für Ihre Datenbank. Dies bedeutet, dass die Regeln getrennt von der Produktlogik definiert werden. Dies hat eine Reihe von Vorteilen: Clients sind nicht für die Durchsetzung der Sicherheit verantwortlich, fehlerhafte Implementierungen gefährden Ihre Daten nicht und, was vielleicht am wichtigsten ist, es ist nicht erforderlich, dass ein zwischengeschalteter Schiedsrichter wie ein Server Daten vor der Welt schützt.

In diesem Thema werden die grundlegende Syntax und Struktur der Sicherheitsregeln für Echtzeitdatenbanken beschrieben, mit denen vollständige Regelsätze erstellt werden.

Strukturierung Ihrer Sicherheitsregeln

Echtzeit-Datenbanksicherheitsregeln bestehen aus JavaScript-ähnlichen Ausdrücken, die in einem JSON-Dokument enthalten sind. Die Struktur Ihrer Regeln sollte der Struktur der Daten entsprechen, die Sie in Ihrer Datenbank gespeichert haben.

Grundregeln identifizieren eine Reihe von zu sichernden Knoten , die beteiligten Zugriffsmethoden (z. B. Lesen, Schreiben) und Bedingungen, unter denen der Zugriff entweder erlaubt oder verweigert wird. In den folgenden Beispielen sind unsere Bedingungen einfache true und false Aussagen. Im nächsten Thema werden wir jedoch dynamischere Möglichkeiten zum Ausdrücken von Bedingungen behandeln.

Wenn wir beispielsweise versuchen, einen child_node unter einem parent_node zu sichern, parent_node die allgemeine Syntax wie folgt:

{
  "rules": {
    "parent_node": {
      "child_node": {
        ".read": <condition>,
        ".write": <condition>,
        ".validate": <condition>,
      }
    }
  }
}

Wenden wir dieses Muster an. Angenommen, Sie verfolgen eine Liste von Nachrichten und haben Daten, die wie folgt aussehen:

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

Ihre Regeln sollten ähnlich aufgebaut sein. Hier finden Sie eine Reihe von Regeln für die schreibgeschützte Sicherheit, die für diese Datenstruktur möglicherweise sinnvoll sind. Dieses Beispiel zeigt, wie wir Datenbankknoten angeben, für die Regeln gelten, und die Bedingungen für die Auswertung von Regeln an diesen Knoten.

{
  "rules": {
    // For requests to access the 'messages' node...
    "messages": {
      // ...and the individual wildcarded 'message' nodes beneath
      // (we'll cover wildcarding variables more a bit later)....
      "$message": {

        // For each message, allow a read operation if <condition>. In this
        // case, we specify our condition as "true", so read access is always granted.
        ".read": "true",

        // For read-only behavior, we specify that for write operations, our
        // condition is false.
        ".write": "false"
      }
    }
  }
}

Grundregeln Operationen

Es gibt drei Arten von Regeln zum Erzwingen der Sicherheit, die auf der Art der Operation basieren, die für die Daten ausgeführt wird: .write , .read und .validate . Hier ist eine kurze Zusammenfassung ihrer Zwecke:

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 aussehen wird, ob er untergeordnete Attribute hat und welchen Datentyp.

Wildcard-Erfassungsvariablen

Alle Regelanweisungen zeigen auf Knoten. Eine Anweisung kann auf einen bestimmten Knoten verweisen oder $ Wildcard- Erfassungsvariablen verwenden , um auf Knotensätze auf einer Hierarchieebene zu verweisen. Verwenden Sie diese Erfassungsvariablen, um den Wert von Knotenschlüsseln zur Verwendung in nachfolgenden Regelanweisungen zu speichern. Diese Technik ermöglicht es Ihnen komplexere Regeln Bedingungen schreiben, etwas , das wir im Detail im nächsten Abschnitt behandeln werden.

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

Kaskade zum Lesen und Schreiben von Regeln

.read und .write Regeln funktionieren von oben nach unten, wobei flachere Regeln tiefere Regeln überschreiben. Wenn eine Regel Lese- oder Schreibberechtigungen für einen bestimmten Pfad gewährt, gewährt sie auch Zugriff auf alle untergeordneten Knoten darunter. Betrachten Sie die folgende Struktur:

{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          /* ignored, since read was allowed already */
          ".read": false
        }
     }
  }
}

Diese Sicherheitsstruktur ermöglicht das Lesen von /bar/ , wenn /foo/ einen baz mit dem Wert true . Die Regel ".read": false unter /foo/bar/ hat hier keine Auswirkung, da der Zugriff nicht durch einen untergeordneten Pfad widerrufen werden kann.

Dies scheint zwar nicht sofort intuitiv zu sein, ist jedoch ein wichtiger Bestandteil der Regelsprache und ermöglicht die Implementierung sehr komplexer Zugriffsrechte mit minimalem Aufwand. Dies wird veranschaulicht, wenn wir später in diesem Handbuch auf die benutzerbasierte Sicherheit eingehen.

Beachten Sie, dass .validate Regeln nicht kaskadieren. Alle Validierungsregeln müssen auf allen Hierarchieebenen erfüllt sein, damit ein Schreibvorgang zulässig ist.

Regeln sind keine Filter

Regeln werden atomar angewendet. Dies bedeutet, dass ein Lese- oder Schreibvorgang sofort fehlschlägt, wenn an diesem Speicherort oder an einem übergeordneten Speicherort keine Regel vorhanden ist, die den Zugriff gewährt. Selbst wenn auf jeden betroffenen untergeordneten Pfad zugegriffen werden kann, schlägt das Lesen am übergeordneten Speicherort vollständig fehl. Betrachten Sie diese Struktur:

{
  "rules": {
    "records": {
      "rec1": {
        ".read": true
      },
      "rec2": {
        ".read": false
      }
    }
  }
}

Ohne zu verstehen, dass Regeln atomar ausgewertet werden, scheint es, als würde das Abrufen des Pfads /records/ rec1 , nicht jedoch rec2 . Das tatsächliche Ergebnis ist jedoch ein Fehler:

JavaScript
var db = firebase.database();
db.ref("records").once("value", function(snap) {
  // success method is not called
}, function(err) {
  // error callback triggered with PERMISSION_DENIED
});
Ziel c
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
  // success block is not called
} withCancelBlock:^(NSError * _Nonnull error) {
  // cancel block triggered with PERMISSION_DENIED
}];
Schnell
var ref = FIRDatabase.database().reference()
ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // success block is not called
}, withCancelBlock: { error in
    // cancel block triggered with PERMISSION_DENIED
})
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // success method is not called
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback triggered with PERMISSION_DENIED
  });
});
SICH AUSRUHEN
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Da die Leseoperation bei /records/ atomar ist und es keine PERMISSION_DENIED gibt, die den Zugriff auf alle Daten unter /records/ gewährt, wird ein PERMISSION_DENIED Fehler PERMISSION_DENIED . Wenn wir diese Regel im Sicherheitssimulator in unserer Firebase-Konsole auswerten, können wir feststellen, dass der Lesevorgang abgelehnt wurde, da keine Leseregel den Zugriff auf den Pfad /records/ erlaubte. Beachten Sie jedoch, dass die Regel für rec1 nie ausgewertet wurde, da sie nicht in dem von uns angeforderten Pfad enthalten war. Um rec1 , müssten wir direkt darauf zugreifen:

JavaScript
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
Ziel c
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Schnell
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records/rec1");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // SUCCESS!
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback is not called
  }
});
SICH AUSRUHEN
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Überlappende Anweisungen

Es ist möglich, dass mehr als eine Regel auf einen Knoten angewendet wird. In dem Fall, in dem mehrere Regelausdrücke einen Knoten identifizieren, wird die Zugriffsmethode verweigert, wenn eine der Bedingungen false :

{
  "rules": {
    "messages": {
      // A rule expression that applies to all nodes in the 'messages' node
      "$message": {
        ".read": "true",
        ".write": "true"
      },
      // A second rule expression applying specifically to the 'message1` node
      "message1": {
        ".read": "false",
        ".write": "false"
      }
    }
  }
}

Im obigen Beispiel werden Lesevorgänge für den Knoten message1 verweigert, da die zweite Regel immer false , obwohl die erste Regel immer true .

Nächste Schritte

Sie können Ihr Verständnis der Sicherheitsregeln für Firebase-Echtzeitdatenbanken vertiefen:

  • Lernen Sie das nächste wichtige Konzept der Regelsprache kennen, dynamische Bedingungen , mit denen Ihre Regeln die Benutzerberechtigung überprüfen, vorhandene und eingehende Daten vergleichen, eingehende Daten validieren, die Struktur der vom Client kommenden Abfragen überprüfen und vieles mehr.

  • Überprüfen Sie typische Sicherheitsanwendungsfälle und die Definitionen der Firebase-Sicherheitsregeln, die diese behandeln .