หน้านี้อิงตามแนวคิดในการจัดโครงสร้างกฎความปลอดภัยและการเขียนเงื่อนไขสำหรับกฎความปลอดภัยเพื่ออธิบายวิธีที่ Cloud Firestore Security Rules โต้ตอบกับคำค้นหา โดยจะอธิบายอย่างละเอียดว่ากฎความปลอดภัยส่งผลต่อคําค้นหาที่คุณเขียนได้อย่างไร และอธิบายวิธีตรวจสอบว่าคําค้นหาใช้ข้อจํากัดเดียวกันกับกฎความปลอดภัย หน้านี้ยังอธิบายวิธีเขียนกฎความปลอดภัยเพื่ออนุญาตหรือปฏิเสธการค้นหาตามพร็อพเพอร์ตี้การค้นหา เช่น limit
และ orderBy
ด้วย
กฎไม่ใช่ตัวกรอง
เมื่อเขียนคำค้นหาเพื่อดึงข้อมูลเอกสาร โปรดทราบว่ากฎความปลอดภัยไม่ใช่ตัวกรอง แต่คำค้นหาจะดึงข้อมูลทั้งหมดหรือไม่ดึงเลย เพื่อประหยัดเวลาและทรัพยากรของคุณ 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 เพื่อตั้งค่าฟิลด์ 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
กับกฎความปลอดภัย
ถูกต้อง: ข้อจำกัดของคำค้นหาตรงกับข้อจำกัดของกฎความปลอดภัย
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
รักษาความปลอดภัยและค้นหาเอกสารตามช่อง
เพื่อแสดงให้เห็นถึงการโต้ตอบระหว่างการค้นหาและกฎเพิ่มเติม กฎความปลอดภัยด้านล่างจะขยายสิทธิ์การอ่านสำหรับคอลเล็กชัน stories
เพื่ออนุญาตให้ผู้ใช้ทุกคนอ่านเอกสาร story
ที่ตั้งค่าฟิลด์ published
เป็น true
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
สำหรับผลลัพธ์ใดๆ ดังนั้น คำค้นหานี้
จึงเป็นไปตามกฎความปลอดภัยและได้รับอนุญาตให้อ่านข้อมูล
คำค้นหา OR
รายการ
เมื่อประเมินการค้นหาเชิงตรรกะ OR
(or
, in
หรือ array-contains-any
)
เทียบกับชุดกฎ Cloud Firestore จะประเมินค่าการเปรียบเทียบแต่ละค่า
แยกกัน ค่าการเปรียบเทียบแต่ละค่าต้องเป็นไปตามข้อจำกัดของกฎความปลอดภัย ตัวอย่างเช่น สำหรับกฎต่อไปนี้
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
ไม่ถูกต้อง: การค้นหาไม่รับประกันว่า
x > 5
สำหรับเอกสารที่อาจเกี่ยวข้องทั้งหมด
// These queries will fail
query(db.collection("mydocuments"),
or(where("x", "==", 1),
where("x", "==", 6)
)
)
query(db.collection("mydocuments"),
where("x", "in", [1, 3, 6, 42, 99])
)
ถูกต้อง: การค้นหาจะรับประกันว่า
x > 5
สำหรับเอกสารที่อาจเกิดขึ้นทั้งหมด
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
การประเมินข้อจำกัดในคำค้นหา
กฎความปลอดภัยยังยอมรับหรือปฏิเสธการค้นหาตามข้อจำกัดได้ด้วย
ตัวแปร 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;
}
}
}
การค้นหากลุ่มคอลเล็กชันและกฎความปลอดภัย
โดยค่าเริ่มต้น คำค้นหาจะกำหนดขอบเขตไว้ที่คอลเล็กชันเดียวและจะดึงผลลัพธ์จากคอลเล็กชันนั้นเท่านั้น การค้นหากลุ่มคอลเล็กชันช่วยให้คุณ ดึงผลลัพธ์จากกลุ่มคอลเล็กชันที่ประกอบด้วยคอลเล็กชันทั้งหมดที่มีรหัสเดียวกันได้ ส่วนนี้อธิบายวิธีรักษาความปลอดภัยให้กับการค้นหากลุ่มคอลเล็กชัน โดยใช้กฎความปลอดภัย
รักษาความปลอดภัยและค้นหาเอกสารตามกลุ่มคอลเล็กชัน
ในกฎความปลอดภัย คุณต้องอนุญาตการค้นหากลุ่มคอลเล็กชันอย่างชัดเจนโดยการเขียนกฎสำหรับกลุ่มคอลเล็กชัน ดังนี้
- ตรวจสอบว่า
rules_version = '2';
เป็นบรรทัดแรกของชุดกฎ การค้นหากลุ่ม คอลเล็กชันต้องใช้ลักษณะการทำงานของไวลด์การ์ดแบบเรียกซ้ำใหม่{name=**}
ของกฎความปลอดภัยเวอร์ชัน 2 - เขียนกฎสำหรับกลุ่มคอลเล็กชันโดยใช้
match /{path=**}/[COLLECTION_ID]/{doc}
ตัวอย่างเช่น ลองพิจารณาฟอรัมที่จัดระเบียบเป็นforum
เอกสารที่มีposts
คอลเล็กชันย่อยดังนี้
/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;
}
}
}
อย่างไรก็ตาม โปรดทราบว่ากฎเหล่านี้จะมีผลกับคอลเล็กชันทั้งหมดที่มีรหัส posts
โดยไม่คำนึงถึงลำดับชั้น
ตัวอย่างเช่น กฎเหล่านี้มีผลกับคอลเล็กชันต่อไปนี้ทั้งหมด
posts
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
รักษาความปลอดภัยให้กับการค้นหากลุ่มคอลเล็กชันตามฟิลด์
เช่นเดียวกับคำค้นหาแบบคอลเล็กชันเดียว คำค้นหากลุ่มคอลเล็กชันต้องเป็นไปตามข้อจำกัดที่กำหนดโดยกฎความปลอดภัยด้วย ตัวอย่างเช่น เราสามารถเพิ่มpublished
ฟิลด์ลงในโพสต์ในฟอรัมแต่ละรายการได้เช่นเดียวกับในตัวอย่างstories
ด้านบน
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
จากนั้นเราจะเขียนกฎสำหรับposts
กลุ่มคอลเล็กชันตามpublished
สถานะและโพสต์author
ได้ดังนี้
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;
}
}
}
กฎเหล่านี้ช่วยให้ไคลเอ็นต์บนเว็บ, 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
เอกสารจากเส้นทางของเอกสาร แต่เราจะทำซ้ำข้อมูลนี้ในแต่ละ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)
บังคับใช้ข้อจํากัดนี้กับคําค้นหาทั้งหมดในคอลเล็กชัน
transactions
กลุ่มเพื่อให้ผู้ใช้รายหนึ่งไม่สามารถดึงข้อมูลเอกสารtransaction
ของผู้ใช้รายอื่นได้
เราบังคับใช้ข้อจำกัดนี้ในกฎความปลอดภัยและรวมการตรวจสอบข้อมูล
สำหรับฟิลด์ 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
}
}
}
ขั้นตอนถัดไป
- ดูตัวอย่างการควบคุมการเข้าถึงตามบทบาทแบบละเอียดเพิ่มเติมได้ที่การรักษาความปลอดภัยให้กับการเข้าถึงข้อมูลสำหรับผู้ใช้และกลุ่ม
- อ่านข้อมูลอ้างอิงเกี่ยวกับกฎความปลอดภัย