Firebase Summit で発表されたすべての情報をご覧ください。Firebase を使用してアプリ開発を加速し、自信を持ってアプリを実行する方法を紹介しています。詳細

リアルタイムデータベースセキュリティルール言語のコア構文を学ぶ

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Firebase Realtime Database セキュリティ ルールを使用すると、データベースに保存されているデータへのアクセスを制御できます。柔軟なルール構文により、データベースへのすべての書き込みから個々のノードでの操作まで、あらゆるものに一致するルールを作成できます。

Realtime Database セキュリティ ルールは、データベースの宣言的な構成です。これは、ルールが製品ロジックとは別に定義されることを意味します。これには多くの利点があります。クライアントはセキュリティを実施する責任がなく、バグのある実装によってデータが危険にさらされることはありません。おそらく最も重要なことは、サーバーなどの中間レフェリーが世界からデータを保護する必要がないことです。

このトピックでは、完全なルールセットを作成するために使用されるリアルタイム データベース セキュリティ ルールの基本的な構文と構造について説明します。

セキュリティ ルールの構造化

Realtime Database セキュリティ ルールは、JSON ドキュメントに含まれる JavaScript に似た式で構成されています。ルールの構造は、データベースに保存したデータの構造に従う必要があります。

基本ルールは、保護するノードのセット、関連するアクセス方法(読み取り、書き込みなど)、およびアクセスを許可または拒否する条件を識別します。次の例では、条件は単純なtruefalseステートメントになりますが、次のトピックでは、条件を表現するより動的な方法について説明します。

したがって、たとえば、 parent_nodechild_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変数を使用して、 widgettitlecolor以外の子がないことを保証する.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
注:この Firebase 製品は、App Clip ターゲットでは使用できません。
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
}];
迅速
注:この Firebase 製品は、App Clip ターゲットでは使用できません。
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
注:この Firebase 製品は、App Clip ターゲットでは使用できません。
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
迅速
注:この Firebase 製品は、App Clip ターゲットでは使用できません。
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 セキュリティ ルールの定義を確認します