Firebase Realtime Database セキュリティ ルールを使用すると、データベースに保存されているデータへのアクセスを制御できます。柔軟なルール構文により、データベースへのすべての書き込みから個々のノードでの操作まで、あらゆるものに一致するルールを作成できます。
Realtime Database セキュリティ ルールは、データベースの宣言的な構成です。これは、ルールが製品ロジックとは別に定義されることを意味します。これには多くの利点があります。クライアントはセキュリティを実施する責任がなく、バグのある実装によってデータが危険にさらされることはありません。おそらく最も重要なことは、サーバーなどの中間レフェリーが世界からデータを保護する必要がないことです。
このトピックでは、完全なルールセットを作成するために使用されるリアルタイム データベース セキュリティ ルールの基本的な構文と構造について説明します。
セキュリティ ルールの構造化
Realtime Database セキュリティ ルールは、JSON ドキュメントに含まれる JavaScript に似た式で構成されています。ルールの構造は、データベースに保存したデータの構造に従う必要があります。
基本ルールは、保護するノードのセット、関連するアクセス方法(読み取り、書き込みなど)、およびアクセスを許可または拒否する条件を識別します。次の例では、条件は単純なtrue
とfalse
ステートメントになりますが、次のトピックでは、条件を表現するより動的な方法について説明します。
したがって、たとえば、 parent_node
をchild_node
の下で保護しようとしている場合、従うべき一般的な構文は次のとおりです。
{ "rules": { "parent_node": { "child_node": { ".read": <condition>, ".write": <condition>, ".validate": <condition>, } } } }
このパターンを当てはめてみましょう。たとえば、メッセージのリストを追跡していて、次のようなデータがあるとします。
{ "messages": { "message0": { "content": "Hello", "timestamp": 1405704370369 }, "message1": { "content": "Goodbye", "timestamp": 1405704395231 }, ... } }
ルールも同様の方法で構成する必要があります。このデータ構造に適した読み取り専用セキュリティの一連のルールを次に示します。この例は、ルールが適用されるデータベース ノードと、それらのノードでルールを評価するための条件を指定する方法を示しています。
{ "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" } } } }
基本ルール操作
データに対して実行される操作のタイプに基づいて、セキュリティを適用するための 3 つのタイプのルールがあります: .write
、 .read
、および.validate
。それらの目的の簡単な要約を次に示します。
ルールの種類 | |
---|---|
。読む | ユーザーによるデータの読み取りが許可されているかどうか、およびいつ許可されているかを示します。 |
。書きます | データの書き込みが許可されているかどうか、およびいつ許可されているかについて説明します。 |
。検証 | 正しくフォーマットされた値がどのように見えるか、子属性を持つかどうか、およびデータ型を定義します。 |
ワイルドカード キャプチャ変数
すべてのルール ステートメントはノードを指します。ステートメントは、特定のノードを指すか、 $
ワイルドカードキャプチャ変数を使用して階層レベルのノード セットを指すことができます。これらのキャプチャ変数を使用して、後続のルール ステートメント内で使用するノード キーの値を格納します。この手法を使用すると、より複雑な Rules条件を記述できます。これについては、次のトピックで詳しく説明します。
{ "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')" } } } } }
動的$
変数は、定数パス名と並行して使用することもできます。この例では、 $other
変数を使用して、 widget
にtitle
とcolor
以外の子がないことを保証する.validate
ルールを宣言しています。追加の子が作成される書き込みは失敗します。
{ "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 } } } }
読み取りおよび書き込みルール カスケード
.read
および.write
ルールはトップダウンで機能し、浅いルールがより深いルールをオーバーライドします。ルールが特定のパスで読み取りまたは書き込み権限を付与する場合、その下にあるすべての子ノードへのアクセスも付与されます。次の構造を検討してください。
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { /* ignored, since read was allowed already */ ".read": false } } } }
このセキュリティ構造により、 /foo/
に値がtrue
の子baz
が含まれている場合はいつでも/bar/
を読み取ることができます。 /foo/bar/
の下の".read": false
ルールは、子パスによってアクセスを取り消すことができないため、ここでは効果がありません。
直観的にはわからないかもしれませんが、これはルール言語の強力な部分であり、最小限の労力で非常に複雑なアクセス権限を実装できます。これについては、このガイドの後半でユーザー ベースのセキュリティを説明するときに説明します。
.validate
ルールはカスケードしないことに注意してください。書き込みを許可するには、階層のすべてのレベルですべての検証ルールを満たす必要があります。
ルールはフィルターではない
ルールはアトミックに適用されます。つまり、アクセスを許可する場所または親の場所にルールがない場合、読み取りまたは書き込み操作はすぐに失敗します。影響を受けるすべての子パスにアクセスできる場合でも、親の場所での読み取りは完全に失敗します。次の構造を検討してください。
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
ルールがアトミックに評価されることを理解していないと、 /records/
パスを取得すると rec1 は返されるがrec1
は返されないように思えるかもしれませrec2
。ただし、実際の結果はエラーです。
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 });
Objective-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 }];
迅速
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 })
ジャワ
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 }); });
残り
curl https://docs-examples.firebaseio.com/rest/records/ # response returns a PERMISSION_DENIED error
/records/
での読み取り操作はアトミックであり、 /records/
の下のすべてのデータへのアクセスを許可する読み取りルールがないため、 PERMISSION_DENIED
エラーがスローされます。 Firebase コンソールのセキュリティ シミュレーターでこのルールを評価すると、 /records/
パスへのアクセスを許可する読み取りルールがないため、読み取り操作が拒否されたことがわかります。ただし、要求したパスにないため、 rec1
のルールは評価されないことに注意してください。 rec1
を取得するには、直接アクセスする必要があります。
JavaScript
var db = firebase.database(); db.ref("records/rec1").once("value", function(snap) { // SUCCESS! }, function(err) { // error callback is not called });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
迅速
var ref = FIRDatabase.database().reference() ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in // SUCCESS! })
ジャワ
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 } });
残り
curl https://docs-examples.firebaseio.com/rest/records/rec1 # SUCCESS!
ステートメントの重複
1 つのノードに複数のルールを適用することができます。複数のルール式がノードを識別する場合、いずれかの条件が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" } } } }
上記の例では、最初のルールが常にtrue
であっても、2 番目のルールが常にfalse
であるため、 message1
ノードへの読み取りは拒否されます。
次のステップ
Firebase Realtime Database セキュリティ ルールの理解を深めることができます。
ルール言語の次の主要な概念である動的条件について学びます。これにより、ルールでユーザーの承認を確認したり、既存のデータと受信データを比較したり、受信データを検証したり、クライアントからのクエリの構造を確認したりできます。
一般的なセキュリティ ユースケースと、それらに対処する Firebase セキュリティ ルールの定義を確認します。