Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Lernen Sie die Kernsyntax der Sprache für Echtzeit-Datenbankregeln

Mit Firebase Realtime Database Security Rules können Sie den Zugriff auf die in Ihrer Datenbank gespeicherten Daten steuern. Die flexible Regelsyntax ermöglicht es Ihnen, Regeln zu erstellen, die zu allem passen, von allen Schreibvorgängen in Ihre Datenbank bis hin zu Operationen auf einzelnen Knoten.

Echtzeit - Datenbank Sicherheitsregeln sind deklarative Konfiguration für Ihre Datenbank. Das 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 werden Ihre Daten nicht gefährden und vielleicht am wichtigsten ist, dass kein zwischengeschalteter Gutachter wie ein Server erforderlich ist, um Daten vor der Welt zu schützen.

In diesem Thema werden die grundlegende Syntax und Struktur von Echtzeit-Datenbanksicherheitsregeln beschrieben, die zum Erstellen vollständiger Regelsätze verwendet werden.

Strukturieren Sie Ihre 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 Menge von Knoten gesichert werden, die Zugriffsmethoden (zB Lesen, Schreiben) beteiligt, und die Bedingungen , unter denen Zugang entweder erlaubt oder verweigert wird . In den folgenden Beispielen werden unsere Bedingungen einfach true und false Aussagen, aber im nächsten Abschnitt werden wir dynamischere Art und Weise auszudrücken Bedingungen abzudecken.

So zum Beispiel, wenn wir ein zu sichern versuchen child_node unter einem parent_node , die allgemeine Syntax zu folgen ist:

{
  "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 in ähnlicher Weise aufgebaut sein. Im Folgenden finden Sie eine Reihe von Regeln für die schreibgeschützte Sicherheit, die für diese Datenstruktur sinnvoll sein könnten. Dieses Beispiel veranschaulicht, 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"
      }
    }
  }
}

Grundregeloperationen

Es gibt drei Arten von Regeln für die Sicherheit auf der Art der Operation anhand der Durchsetzung auf den Daten durchgeführt werden: .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 aussieht, ob er untergeordnete Attribute hat und den Datentyp.

Wildcard-Capture-Variablen

Alle Regelanweisungen zeigen auf Knoten. Eine Anweisung kann an einen bestimmten Knotenpunkt oder verwendet $ Platzhalter capture Variablen Punkt auf Sätze von Knoten auf einer Ebene der Hierarchie. Verwenden Sie diese Capture-Variablen, um den Wert von Knotenschlüsseln zur Verwendung in nachfolgenden Rules-Anweisungen 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 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 }
    }
  }
}

Lese- und Schreibregelkaskade

.read und .write Regeln arbeiten von oben nach unten, mit flacheren Regeln zwingender tiefe Regeln. Wenn eine Regel Zuschüsse an einem bestimmten Pfad Lese- oder Schreibberechtigungen, dann räumt er 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 erlaubt /bar/ zu aus gelesen werden , wenn /foo/ enthält ein Kind baz mit dem Wert true . Die ".read": false Regel unter /foo/bar/ hat keinen Einfluss hier, da der Zugang nicht von einem Kind Weg widerrufen werden kann.

Auch wenn es nicht sofort intuitiv erscheinen mag, ist dies ein mächtiger Teil 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 tun. Alle Validierungsregeln müssen auf allen Ebenen der Hierarchie erfüllt sein, damit ein Schreibvorgang zulässig ist.

Regeln sind keine Filter

Regeln werden auf atomare Weise angewendet. Das bedeutet, dass ein Lese- oder Schreibvorgang sofort fehlschlägt, wenn es an diesem Standort oder an einem übergeordneten Standort keine Regel gibt, die den Zugriff gewährt. Selbst wenn auf alle betroffenen untergeordneten Pfade 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 die Regeln atomar ausgewertet werden, könnte es scheinen , wie das Abrufen der /records/ Pfad zurückkehren würde rec1 aber nicht rec2 . Das eigentliche 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 der Lesevorgang bei /records/ atomar ist , und es gibt keine Lese Regel , dass gewährt den Zugriff auf alle Daten unter /records/ , dies einen werfen PERMISSION_DENIED Fehler. Wenn wir diese Regel im Sicherheits Simulator in unserer bewertenen Firebase - /records/ Konsole , können wir sehen , dass die Leseoperation verweigert wurde , weil keine Lese Regel Zugriff auf den erlaubten /records/ Pfad. Beachten Sie jedoch, dass die Regel für rec1 nie ausgewertet worden , weil es nicht in dem Weg , den wir angefordert. Holen rec1 , müssten wir es direkt zugreifen zu können :

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 Aussagen

Es ist möglich, dass mehrere Regeln auf einen Knoten angewendet werden. In dem Fall, dass mehrere Regeln Ausdrücke identifizieren einen Knoten, wird die Zugriffsverfahren verweigert , wenn eine der Bedingungen ist 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"
      }
    }
  }
}

In dem obigen Beispiel liest auf den message1 Knoten verweigert werden , weil die zweiten Regeln immer false , auch wenn die erste Regel ist immer true .

Nächste Schritte

Sie können Ihr Verständnis der Firebase Realtime Database Security Rules vertiefen:

  • Lernen Sie das nächste große Konzept der Sprachregeln, dynamischer Bedingungen , die Ihre Regeln Prüfung Benutzerberechtigung lassen, zu vergleichen , bestehende und eingehende Daten, zu validieren eingehenden Daten, überprüfen Sie die Struktur der Anfragen vom Client kommen, und vieles mehr.

  • Überprüfen Sie typische Sicherheits Use Cases und die Firebase Sicherheitsregeln Definitionen , die Adresse sie .