データに安全にクエリを実行する

このページでは、セキュリティ ルールの構造化セキュリティ ルールの条件の記述のコンセプトを基に、Cloud Firestore のセキュリティ ルールとクエリがどのように関係しているかを説明します。作成可能なクエリにセキュリティ ルールがどのように影響するかを詳しく見ていき、セキュリティ ルールと同じ制約をクエリで使用する方法を説明します。また、limitorderBy などのクエリ プロパティに基づいてクエリを許可または拒否するセキュリティ ルールを記述する方法についても説明します。

クエリとセキュリティ ルール

ドキュメントを取得するクエリを記述するとき、セキュリティ ルールはフィルタではないことにご注意ください。セキュリティ ルールは、クエリが完全に動作するか、まったく動作しないかを決定します。時間とリソースを節約するため、Cloud Firestore はすべてのドキュメントの実際のフィールドの値ではなく、結果セットの可能性に対してクエリを評価します。クライアントが読み取り権限を持たないドキュメントをクエリが返す可能性がある場合、リクエスト全体が失敗します。

以下の例で示すように、セキュリティ ルールの制約に合わせてクエリを記述する必要があります。

auth.uid に基づいてドキュメントにセキュリティを適用し、クエリを実行する

次の例は、セキュリティ ルールで保護されたドキュメントを取得するクエリを記述する方法を示しています。story ドキュメントのコレクションを含むデータベースを考えてみましょう。

/stories/{storyid}

{
  title: "A Great Story",
  content: "Once upon a time...",
  author: "some_auth_id",
  published: false
}

各ドキュメントには title フィールドと content フィールドに加えて、アクセス制御に使用される author フィールドと published フィールドが格納されています。ここで示す例では、アプリが Firebase Authentication を使用して、ドキュメントを作成したユーザーの UID を author フィールドに設定していると想定しています。また、Firebase Authentication はセキュリティ ルールの request.auth 変数に値を設定します。

次のセキュリティ ルールでは、request.auth 変数と resource.data 変数を使用して、各 story に対する読み取りと書き込みのアクセス権を作成者のみに制限しています。

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Only the authenticated user who authored the document can read or write
      allow read, write: if request.auth.uid == resource.data.author;
    }
  }
}

あるアプリに、ユーザーが作成した story ドキュメントのリストを表示するページを含めるとします。次のクエリを使用してこのページのデータを取得できそうに思えますが、セキュリティ ルールと同じ制約が含まれていないため、このクエリは失敗します。

無効: クエリの制約がセキュリティ ルールの制約と一致していない

// This query will fail
db.collection("stories").get()

現在のユーザーがすべての story ドキュメントの作成者であったとしても、このクエリは失敗します。この動作の理由は、Cloud Firestore がセキュリティ ルールを適用するときには、データベース内のドキュメントの実際のプロパティに対してではなく、結果セットの「可能性」に対してクエリを評価するためです。セキュリティ ルールに違反するドキュメントがクエリに含まれる可能性がある場合、クエリは失敗します。

これとは対照的に、次の例では author フィールドに関してセキュリティ ルールと同じ制約が含まれているため、このクエリは成功します。

有効: クエリの制約がセキュリティ ルールの制約と一致している

var user = firebase.auth().currentUser;

db.collection("stories").where("author", "==", user.uid).get()

フィールドに基づいてドキュメントにセキュリティを適用し、クエリを実行する

クエリとルールの関係の例をさらに示します。次のセキュリティ ルールでは、stories コレクションの読み取りアクセス対象を広げ、published フィールドが true に設定されている story ドキュメントをすべてのユーザーが読み取ることができるようにします。

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Anyone can read a published story; only story authors can read unpublished stories
      allow read: if resource.data.published == true || request.auth.uid == resource.data.author;
      // Only story authors can write
      allow write: if request.auth.uid == resource.data.author;
    }
  }
}

公開ページのクエリには、セキュリティ ルールと同じ制約を含める必要があります。

db.collection("stories").where("published", "==", true).get()

クエリの制約 .where("published", "==", true) は、返されるすべての結果で resource.data.publishedtrue であることを保証します。したがって、このクエリはセキュリティ ルールに合致し、データを読み取ることができます。

クエリの制約の評価

セキュリティ ルールは、クエリの制約に基づいてそのクエリを許可または拒否することもできます。request.query 変数には、クエリの limitoffsetorderBy プロパティが含まれており、たとえば、取得されるドキュメントの最大数を特定の範囲に制限していないクエリをセキュリティ ルールで拒否できます。

allow list: if request.query.limit <= 10;

次のルールセットは、クエリ内の制約を評価するセキュリティ ルールを作成する方法を示しています。この例では、前出の stories ルールセットを拡張し、次の変更を加えています。

  • このルールセットでは read ルールを get ルールと list ルールに分割しています。
  • get ルールでは、単一のドキュメントの取得を公開ドキュメントまたは該当ユーザーが作成したドキュメントに制限しています。
  • list ルールでは、get ルールと同じ制限をクエリに適用しています。また、クエリの制限をチェックし、無制限のクエリまたは制限が 10 を超えるクエリを拒否します。
  • このルールセットではコードの重複を避けるために authorOrPublished() 関数を定義しています。
service cloud.firestore {

  match /databases/{database}/documents {

    match /stories/{storyid} {

      // Returns `true` if the requested story is 'published'
      // or the user authored the story
      function authorOrPublished() {
        return resource.data.published == true || request.auth.uid == resource.data.author;
      }

      // Deny any query not limited to 10 or fewer documents
      // Anyone can query published stories
      // Authors can query their unpublished stories
      allow list: if request.query.limit <= 10 &&
                     authorOrPublished();

      // Anyone can retrieve a published story
      // Only a story's author can retrieve an unpublished story
      allow get: if authorOrPublished();

      // Only a story's authors can write to a story
      allow write: if request.auth.uid == resource.data.author;
    }

  }
}

次のステップ

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。