このページは、「セキュリティ ルールの構造化」と「セキュリティ ルールの条件の記述」の概念に基づいて構築されており、Cloud Firestore セキュリティ ルールがクエリとどのように相互作用するかを説明します。セキュリティ ルールが記述可能なクエリにどのように影響するかを詳しく見ていき、セキュリティ ルールと同じ制約をクエリで確実に使用する方法について説明します。このページでは、 limit
やorderBy
などのクエリ プロパティに基づいてクエリを許可または拒否するセキュリティ ルールを作成する方法についても説明します。
ルールはフィルターではありません
ドキュメントを取得するためのクエリを作成するときは、セキュリティ ルールはフィルターではないことに注意してください。クエリはすべてかゼロかです。時間とリソースを節約するために、Cloud Firestore は、すべてのドキュメントの実際のフィールド値ではなく、潜在的な結果セットに対してクエリを評価します。クライアントが読み取り権限を持っていないドキュメントをクエリが返す可能性がある場合、リクエスト全体が失敗します。
クエリとセキュリティ ルール
以下の例が示すように、セキュリティ ルールの制約に適合するようにクエリを記述する必要があります。
auth.uid
に基づくドキュメントの保護とクエリ
次の例は、セキュリティ ルールによって保護されたドキュメントを取得するためのクエリを記述する方法を示しています。 story
ドキュメントのコレクションを含むデータベースを考えてみましょう。
/stories/{ストーリーID}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
各ドキュメントには、 title
フィールドとcontent
フィールドに加えて、アクセス制御に使用する作成author
フィールドとpublished
フィールドが格納されます。これらの例では、アプリがFirebase Authenticationを使用してauthor
フィールドをドキュメントを作成したユーザーの UID に設定することを前提としています。 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 != null && request.auth.uid == resource.data.author;
}
}
}
アプリに、ユーザーが作成したstory
ドキュメントのリストをユーザーに表示するページが含まれているとします。次のクエリを使用してこのページにデータを入力できると思うかもしれません。ただし、セキュリティ ルールと同じ制約が含まれていないため、このクエリは失敗します。
無効: クエリの制約がセキュリティ ルールの制約と一致しません
// This query will fail
db.collection("stories").get()
現在のユーザーが実際にはすべてのstory
ドキュメントの作成者であっても、クエリは失敗します。この動作の理由は、Cloud Firestore がセキュリティ ルールを適用するときに、データベース内のドキュメントの実際のプロパティではなく、潜在的な結果セットに対してクエリを評価するためです。セキュリティ ルールに違反するドキュメントがクエリに含まれる可能性がある場合、クエリは失敗します。
対照的に、次のクエリは成功します。これは、 author
フィールドにセキュリティ ルールと同じ制約が含まれているためです。
Valid : クエリの制約がセキュリティ ルールの制約と一致する
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 != null && request.auth.uid == resource.data.author);
// Only story authors can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
公開されたページのクエリには、セキュリティ ルールと同じ制約が含まれている必要があります。
db.collection("stories").where("published", "==", true).get()
クエリ制約.where("published", "==", true)
は、すべての結果に対してresource.data.published
がtrue
であることを保証します。したがって、このクエリはセキュリティ ルールを満たし、データの読み取りが許可されます。
in
およびarray-contains-any
クエリ
ルールセットに対してin
またはarray-contains-any
クエリ句を評価する場合、Cloud Firestore は各比較値を個別に評価します。各比較値は、セキュリティ ルールの制約を満たす必要があります。たとえば、次のルールの場合:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
無効: クエリは、すべての潜在的なドキュメントに対してx > 5
であることを保証しません
// This query will fail
db.collection("mydocuments").where("x", "in", [1, 3, 6, 42, 99]).get()
Valid : クエリは、すべての潜在的なドキュメントに対してx > 5
であることを保証します
db.collection("mydocuments").where("x", "in", [6, 42, 99, 105, 200]).get()
クエリの制約の評価
セキュリティ ルールは、制約に基づいてクエリを許可または拒否することもできます。 request.query
変数には、クエリのlimit
、 offset
、およびorderBy
プロパティが含まれています。たとえば、セキュリティ ルールは、取得するドキュメントの最大数を特定の範囲に制限しないクエリを拒否できます。
allow list: if request.query.limit <= 10;
次のルールセットは、クエリに課された制約を評価するセキュリティ ルールを記述する方法を示しています。この例では、次の変更を加えて以前stories
ルールセットを拡張します。
- ルールセットは、読み取りルールを
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 author can write to a story
allow write: if request.auth.uid == resource.data.author;
}
}
}
コレクション グループのクエリとセキュリティ ルール
デフォルトでは、クエリは 1 つのコレクションに限定され、そのコレクションからのみ結果を取得します。コレクション グループ クエリを使用すると、同じ ID を持つすべてのコレクションで構成されるコレクション グループから結果を取得できます。このセクションでは、セキュリティ ルールを使用してコレクション グループ クエリをセキュリティで保護する方法について説明します。
コレクション グループに基づいてドキュメントを保護し、クエリを実行する
セキュリティ ルールでは、コレクション グループのルールを記述して、コレクション グループ クエリを明示的に許可する必要があります。
-
rules_version = '2';
あることを確認してください。ルールセットの最初の行です。コレクション グループ クエリには、セキュリティ ルール バージョン 2 の新しい再帰ワイルドカード{name=**}
動作が必要です。 -
match /{path=**}/ [COLLECTION_ID] /{doc}
を使用して、コレクション グループのルールを記述します。
たとえば、 posts
サブコレクションを含むforum
ドキュメントに編成されたフォーラムを考えてみましょう。
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
このアプリケーションでは、投稿を所有者が編集可能にし、認証されたユーザーが読めるようにします。
service cloud.firestore {
match /databases/{database}/documents {
match /forums/{forumid}/posts/{post} {
// Only authenticated users can read
allow read: if request.auth != null;
// Only the post author can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
認証されたユーザーは、任意の単一のフォーラムの投稿を取得できます。
db.collection("forums/technology/posts").get()
しかし、現在のユーザーにすべてのフォーラムでの投稿を表示したい場合はどうすればよいでしょうか?コレクション グループ クエリを使用して、すべてのposts
コレクションから結果を取得できます。
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
セキュリティ ルールで、 posts
コレクション グループの読み取りルールまたはリスト ルールを記述して、このクエリを許可する必要があります。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Authenticated users can query the posts collection group
// Applies to collection queries, collection group queries, and
// single document retrievals
match /{path=**}/posts/{post} {
allow read: if request.auth != null;
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
ただし、これらのルールは、階層に関係なく、ID posts
を持つすべてのコレクションに適用されることに注意してください。たとえば、これらのルールは次のすべてのposts
コレクションに適用されます。
-
/posts/{postid}
-
/forums/{forumid}/posts/{postid}
-
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
フィールドに基づくセキュアなコレクション グループ クエリ
単一コレクション クエリと同様に、コレクション グループ クエリも、セキュリティ ルールによって設定された制約を満たす必要があります。たとえば、上記のstories
例で行ったように、各フォーラム投稿にpublished
フィールドを追加できます。
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
次に、 published
たステータスと投稿author
に基づいて、 posts
コレクション グループのルールを記述できます。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Returns `true` if the requested post is 'published'
// or the user authored the post
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
match /{path=**}/posts/{post} {
// Anyone can query published posts
// Authors can query their unpublished posts
allow list: if authorOrPublished();
// Anyone can retrieve a published post
// Authors can retrieve an unpublished post
allow get: if authorOrPublished();
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth.uid == resource.data.author;
}
}
}
これらのルールを使用すると、Web、Apple、および Android クライアントは次のクエリを実行できます。
誰でもフォーラムで公開された投稿を取得できます。
db.collection("forums/technology/posts").where('published', '==', true).get()
誰でも、すべてのフォーラムで著者の公開された投稿を取得できます。
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
作成者は、すべてのフォーラムで公開済みおよび未公開のすべての投稿を取得できます。
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
コレクション グループとドキュメント パスに基づいてドキュメントを保護し、クエリを実行する
場合によっては、ドキュメント パスに基づいてコレクション グループのクエリを制限する必要があります。これらの制限を作成するには、フィールドに基づいてドキュメントを保護およびクエリするのと同じ手法を使用できます。
複数の証券取引所と暗号通貨取引所の間で各ユーザーの取引を追跡するアプリケーションを考えてみましょう。
/users/{userid}/exchange/{exchangeid}/transactions/{transaction}
{
amount: 100,
exchange: 'some_exchange_name',
timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
user: "some_auth_id",
}
user
フィールドに注意してください。ドキュメントのパスからどのユーザーがtransaction
ドキュメントを所有しているかがわかりますが、次の 2 つのことができるため、各transaction
ドキュメントでこの情報を複製します。
ドキュメント パスに特定の
/users/{userid}
を含むドキュメントに制限されたコレクション グループ クエリを記述します。例えば:var user = firebase.auth().currentUser; // Return current user's last five transactions across all exchanges db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
あるユーザーが別のユーザーの
transaction
ドキュメントを取得できないように、transactions
コレクション グループのすべてのクエリにこの制限を適用します。
セキュリティ ルールでこの制限を適用し、 user
フィールドのデータ検証を含めます。
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{path=**}/transactions/{transaction} {
// Authenticated users can retrieve only their own transactions
allow read: if resource.data.user == request.auth.uid;
}
match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
// Authenticated users can write to their own transactions subcollections
// Writes must populate the user field with the correct auth id
allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
}
}
}
次のステップ
- 役割ベースのアクセス制御の詳細な例については、「ユーザーとグループのデータ アクセスを保護する」を参照してください。
- セキュリティ ルール リファレンスをお読みください。