Firebase Realtime Database Security Rulesを使用すると、データベースに保存されているデータへのアクセスを制御できます。柔軟なルール構文を使用すると、データベースへのすべての書き込みから個々のノードでの操作まで、あらゆるものに一致するルールを作成できます。
リアルタイムデータベースセキュリティルールは、データベースの宣言型構成です。これは、ルールが製品ロジックとは別に定義されていることを意味します。これには多くの利点があります。クライアントはセキュリティの実施に責任を負わず、バグのある実装はデータを危険にさらすことはありません。おそらく最も重要なことは、サーバーなどの中間の審判がデータを世界から保護する必要がないことです。
このトピックでは、完全なルールセットを作成するために使用される基本的な構文と構造のリアルタイムデータベースセキュリティルールについて説明します。
セキュリティルールの構築
リアルタイムデータベースセキュリティルールは、JSONドキュメントに含まれるJavaScriptのような式で構成されています。ルールの構造は、データベースに保存したデータの構造に従う必要があります。
基本的なルールは、保護するノードのセット、関連するアクセス方法(読み取り、書き込みなど)、およびアクセスが許可または拒否される条件を識別します。次の例では、条件は単純なtrue
とfalse
ステートメントになりますが、次のトピックでは、条件を表現するためのより動的な方法について説明します。
したがって、たとえば、 child_node
の下でparent_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" } } } }
基本的なルールの操作
データに対して実行される操作のタイプに基づいてセキュリティを適用するためのルールには、 .write
、 .read
、および.validate
の3つのタイプがあります。それらの目的の簡単な要約は次のとおりです。
ルールタイプ | |
---|---|
。読む | ユーザーがデータを読み取ることができるかどうか、またいつ許可されるかについて説明します。 |
。書きます | データの書き込みが許可されるかどうか、いつ許可されるかを説明します。 |
。検証 | 正しくフォーマットされた値がどのように見えるか、子属性があるかどうか、およびデータ型を定義します。 |
ワイルドカードキャプチャ変数
すべてのルールステートメントはノードを指します。ステートメントは、特定のノードを指すか、 $
ワイルドカードキャプチャ変数を使用して、階層のレベルにあるノードのセットを指すことができます。これらのキャプチャ変数を使用して、後続のルールステートメント内で使用するノードキーの値を格納します。この手法を使用すると、より複雑なルール条件を記述できます。これについては、次のトピックで詳しく説明します。
{ "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
が返されるが、 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 })
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 }); });
休み
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! })
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 } });
休み
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
ノードへの読み取りは拒否されます。
次のステップ
FirebaseRealtimeデータベースのセキュリティルールについての理解を深めることができます。
ルール言語の次の主要な概念である動的条件について学習します。これにより、ルールでユーザー承認の確認、既存のデータと受信データの比較、受信データの検証、クライアントからのクエリの構造の確認などが可能になります。
一般的なセキュリティのユースケースと、それらに対処するFirebaseセキュリティルールの定義を確認します。