คู่มือนี้ต่อยอดจากคู่มือภาษาหลักของกฎการรักษาความปลอดภัยของ Firebase เพื่อแสดงวิธีเพิ่มเงื่อนไขลงในกฎการรักษาความปลอดภัยของฐานข้อมูลเรียลไทม์ของ Firebase
องค์ประกอบที่ใช้สร้างสรรค์หลักของกฎการรักษาความปลอดภัยของ Realtime Database คือเงื่อนไข เงื่อนไขคือนิพจน์บูลีนที่กำหนดว่าจะอนุญาตหรือปฏิเสธการดำเนินการหนึ่งๆ สำหรับกฎพื้นฐาน การใช้ตัวอักษร true และ false เป็นเงื่อนไขนั้นใช้ได้ดี แต่ภาษาของกฎการรักษาความปลอดภัยของ Realtime Database มีวิธีเขียนเงื่อนไขที่ซับซ้อนมากขึ้นซึ่งทำสิ่งต่อไปนี้ได้
- ตรวจสอบการตรวจสอบสิทธิ์ผู้ใช้
- ประเมินข้อมูลที่มีอยู่กับข้อมูลที่ส่งเข้ามาใหม่
- เข้าถึงและเปรียบเทียบส่วนต่างๆ ของฐานข้อมูล
- ตรวจสอบข้อมูลขาเข้า
- ใช้โครงสร้างของการค้นหาขาเข้าสำหรับตรรกะความปลอดภัย
การใช้ตัวแปร $ เพื่อจับภาพส่วนเส้นทาง
คุณสามารถจับภาพบางส่วนของเส้นทางสำหรับการอ่านหรือเขียนได้โดยประกาศตัวแปรการจับภาพด้วยคำนำหน้า $
ซึ่งทำหน้าที่เป็นไวลด์การ์ดและจัดเก็บค่าของคีย์นั้นเพื่อใช้ภายในเงื่อนไขของกฎ
{ "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 เพื่อประกาศ
กฎ .validate ที่ช่วยให้มั่นใจว่า
widget ไม่มีรายการย่อยอื่นนอกเหนือจาก title และ color
การเขียนใดๆ ที่จะส่งผลให้มีการสร้างรายการย่อยเพิ่มเติมจะล้มเหลว
{
"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 }
}
}
}การตรวจสอบสิทธิ์
รูปแบบกฎการรักษาความปลอดภัยที่พบบ่อยที่สุดอย่างหนึ่งคือการควบคุมการเข้าถึงตามสถานะการตรวจสอบสิทธิ์ของผู้ใช้ เช่น แอปของคุณอาจต้องการอนุญาตให้เฉพาะผู้ใช้ที่ลงชื่อเข้าใช้เขียนข้อมูลได้
หากแอปของคุณใช้การตรวจสอบสิทธิ์ Firebase ตัวแปร request.auth จะมีข้อมูลการตรวจสอบสิทธิ์สำหรับไคลเอ็นต์ที่ขอข้อมูล
ดูข้อมูลเพิ่มเติมเกี่ยวกับ request.auth ได้ใน เอกสารอ้างอิง
Firebase Authentication ทำงานร่วมกับ Firebase Realtime Database เพื่อให้คุณควบคุมการเข้าถึงข้อมูล
ตามผู้ใช้แต่ละรายได้โดยใช้เงื่อนไข เมื่อผู้ใช้ตรวจสอบสิทธิ์แล้ว ตัวแปร auth ในกฎการรักษาความปลอดภัยของ Realtime Database จะได้รับการป้อนข้อมูลของผู้ใช้ ข้อมูลนี้รวมถึงตัวระบุที่ไม่ซ้ำกัน (uid) รวมถึงข้อมูลบัญชีที่ลิงก์ไว้ เช่น รหัส Facebook หรืออีเมล และข้อมูลอื่นๆ หากคุณใช้ผู้ให้บริการการตรวจสอบสิทธิ์ที่กำหนดเอง คุณสามารถเพิ่มช่องของคุณเองลงในเพย์โหลดการตรวจสอบสิทธิ์ของผู้ใช้ได้
ส่วนนี้อธิบายวิธีรวมภาษาของกฎการรักษาความปลอดภัยของฐานข้อมูลเรียลไทม์ของ Firebase กับข้อมูลการตรวจสอบสิทธิ์เกี่ยวกับผู้ใช้ การรวมแนวคิดทั้ง 2 นี้เข้าด้วยกันจะช่วยให้คุณควบคุมการเข้าถึงข้อมูลตามข้อมูลประจำตัวของผู้ใช้ได้
ตัวแปร auth
ตัวแปร auth ที่กำหนดไว้ล่วงหน้าในกฎจะเป็นค่าว่างก่อนที่จะมีการตรวจสอบสิทธิ์
เมื่อผู้ใช้ได้รับการตรวจสอบสิทธิ์ด้วย การตรวจสอบสิทธิ์ Firebase ตัวแปรนี้จะมีแอตทริบิวต์ต่อไปนี้
| provider | วิธีการตรวจสอบสิทธิ์ที่ใช้ ("password", "anonymous", "facebook", "github", "google", หรือ "twitter") |
| uid | รหัสผู้ใช้ที่ไม่ซ้ำกัน ซึ่งรับประกันได้ว่าไม่ซ้ำกันในผู้ให้บริการทุกราย |
| token |
เนื้อหาของโทเค็นรหัส Firebase Auth ดูรายละเอียดเพิ่มเติมได้ในเอกสารอ้างอิงสำหรับ
auth.token
|
ต่อไปนี้เป็นตัวอย่างกฎที่ใช้ตัวแปร auth เพื่อให้มั่นใจว่าผู้ใช้แต่ละรายจะเขียนได้เฉพาะเส้นทางที่เจาะจงของผู้ใช้เท่านั้น
{
"rules": {
"users": {
"$user_id": {
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "$user_id === auth.uid"
}
}
}
}การจัดโครงสร้างฐานข้อมูลเพื่อรองรับเงื่อนไขการตรวจสอบสิทธิ์
โดยปกติแล้ว การจัดโครงสร้างฐานข้อมูลในลักษณะที่ทำให้การเขียน
Security Rulesง่ายขึ้นจะเป็นประโยชน์ รูปแบบทั่วไปอย่างหนึ่งสำหรับการจัดเก็บข้อมูลผู้ใช้ใน Realtime Database คือ
การจัดเก็บผู้ใช้ทั้งหมดไว้ในโหนด users โหนดเดียว ซึ่งรายการย่อยคือ
ค่า uid ของผู้ใช้แต่ละราย หากต้องการจำกัดการเข้าถึงข้อมูลนี้เพื่อให้เฉพาะผู้ใช้ที่เข้าสู่ระบบเท่านั้นที่ดูข้อมูลของตนเองได้ กฎของคุณจะมีลักษณะดังนี้
{
"rules": {
"users": {
"$uid": {
".read": "auth !== null && auth.uid === $uid"
}
}
}
}การใช้การอ้างสิทธิ์ที่กำหนดเองสำหรับการตรวจสอบสิทธิ์
สำหรับแอปที่ต้องมีการควบคุมการเข้าถึงที่กำหนดเองสำหรับผู้ใช้ที่แตกต่างกัน Firebase Authentication
ช่วยให้นักพัฒนาแอปตั้งค่าการอ้างสิทธิ์ในผู้ใช้ Firebase ได้
การอ้างสิทธิ์เหล่านี้เข้าถึงได้ในตัวแปร auth.token ในกฎ
ต่อไปนี้เป็นตัวอย่างกฎที่ใช้การอ้างสิทธิ์ที่กำหนดเอง hasEmergencyTowel
{
"rules": {
"frood": {
// A towel is about the most massively useful thing an interstellar
// hitchhiker can have
".read": "auth.token.hasEmergencyTowel === true"
}
}
}นักพัฒนาแอปที่สร้าง โทเค็นการตรวจสอบสิทธิ์ที่กำหนดเองสามารถเลือกเพิ่มการอ้างสิทธิ์ลงในโทเค็นเหล่านี้ได้ การอ้างสิทธิ์เหล่านี้จะอยู่ในตัวแปร auth.token ในกฎ
ข้อมูลที่มีอยู่เทียบกับข้อมูลใหม่
ตัวแปร data ที่กำหนดไว้ล่วงหน้าใช้เพื่ออ้างอิงข้อมูลก่อนที่จะมีการดำเนินการเขียน ในทางกลับกัน ตัวแปร newData จะมีข้อมูลใหม่ที่จะมีอยู่หากการดำเนินการเขียนสำเร็จ
newData แสดงถึงผลลัพธ์ที่ผสานรวมของข้อมูลใหม่ที่กำลังเขียนและข้อมูลที่มีอยู่
ตัวอย่างเช่น กฎนี้จะอนุญาตให้เราสร้างระเบียนใหม่หรือลบระเบียนที่มีอยู่ แต่ไม่อนุญาตให้ทำการเปลี่ยนแปลงข้อมูลที่มีอยู่ซึ่งไม่ใช่ค่าว่าง
// we can write as long as old data or new data does not exist // in other words, if this is a delete or a create, but not an update ".write": "!data.exists() || !newData.exists()"
การอ้างอิงข้อมูลในเส้นทางอื่น
คุณสามารถใช้ข้อมูลใดก็ได้เป็นเกณฑ์สำหรับกฎ การใช้ตัวแปร root, data และ newData ที่กำหนดไว้ล่วงหน้าจะช่วยให้เราเข้าถึงเส้นทางใดก็ได้ตามที่จะมีอยู่ก่อนหรือหลังเหตุการณ์การเขียน
ลองดูตัวอย่างนี้ ซึ่งอนุญาตการดำเนินการเขียนตราบใดที่ค่าของโหนด /allow_writes/ เป็น true โหนดระดับบนไม่มีการตั้งค่าแฟล็ก readOnly และมีรายการย่อยชื่อ foo ในข้อมูลที่เขียนใหม่
".write": "root.child('allow_writes').val() === true &&
!data.parent().child('readOnly').exists() &&
newData.child('foo').exists()"การตรวจสอบข้อมูล
การบังคับใช้โครงสร้างข้อมูลและการตรวจสอบรูปแบบและเนื้อหาของข้อมูลควรทำโดยใช้กฎ .validate ซึ่งจะทำงานหลังจากกฎ .write ทำงานสำเร็จเพื่อให้สิทธิ์เข้าถึงเท่านั้น ด้านล่างนี้เป็นตัวอย่างคำจำกัดความของกฎ .validate ซึ่งอนุญาตเฉพาะวันที่ในรูปแบบ YYYY-MM-DD ระหว่างปี 1900-2099 เท่านั้น ซึ่งตรวจสอบโดยใช้นิพจน์ทั่วไป
".validate": "newData.isString() &&
newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"กฎ .validate เป็นกฎการรักษาความปลอดภัยประเภทเดียวที่ไม่มีการทำงานแบบเรียงซ้อน หากกฎการตรวจสอบใดๆ ล้มเหลวในระเบียนย่อย การดำเนินการเขียนทั้งหมดจะถูกปฏิเสธ
นอกจากนี้ ระบบจะละเว้นคำจำกัดความของการตรวจสอบเมื่อมีการลบข้อมูล (นั่นคือ เมื่อค่าใหม่ที่เขียนเป็น null)
จุดเหล่านี้อาจดูเหมือนเป็นจุดเล็กๆ น้อยๆ แต่จริงๆ แล้วเป็นฟีเจอร์ที่สำคัญสำหรับการเขียนกฎการรักษาความปลอดภัยของฐานข้อมูลเรียลไทม์ของ Firebase ที่มีประสิทธิภาพ ลองดูกฎต่อไปนี้
{
"rules": {
// write is allowed for all paths
".write": true,
"widget": {
// a valid widget must have attributes "color" and "size"
// allows deleting widgets (since .validate is not applied to delete rules)
".validate": "newData.hasChildren(['color', 'size'])",
"size": {
// the value of "size" must be a number between 0 and 99
".validate": "newData.isNumber() &&
newData.val() >= 0 &&
newData.val() <= 99"
},
"color": {
// the value of "color" must exist as a key in our mythical
// /valid_colors/ index
".validate": "root.child('valid_colors/' + newData.val()).exists()"
}
}
}
}เมื่อพิจารณาถึงตัวแปรนี้แล้ว ให้ดูผลลัพธ์ของการดำเนินการเขียนต่อไปนี้
JavaScript
var ref = db.ref("/widget"); // PERMISSION_DENIED: does not have children color and size ref.set('foo'); // PERMISSION DENIED: does not have child color ref.set({size: 22}); // PERMISSION_DENIED: size is not a number ref.set({ size: 'foo', color: 'red' }); // SUCCESS (assuming 'blue' appears in our colors list) ref.set({ size: 21, color: 'blue'}); // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child('size').set(99);
Objective-C
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"]; // PERMISSION_DENIED: does not have children color and size [ref setValue: @"foo"]; // PERMISSION DENIED: does not have child color [ref setValue: @{ @"size": @"foo" }]; // PERMISSION_DENIED: size is not a number [ref setValue: @{ @"size": @"foo", @"color": @"red" }]; // SUCCESS (assuming 'blue' appears in our colors list) [ref setValue: @{ @"size": @21, @"color": @"blue" }]; // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate [[ref child:@"size"] setValue: @99];
Swift
var ref = FIRDatabase.database().reference().child("widget") // PERMISSION_DENIED: does not have children color and size ref.setValue("foo") // PERMISSION DENIED: does not have child color ref.setValue(["size": "foo"]) // PERMISSION_DENIED: size is not a number ref.setValue(["size": "foo", "color": "red"]) // SUCCESS (assuming 'blue' appears in our colors list) ref.setValue(["size": 21, "color": "blue"]) // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child("size").setValue(99);
Java
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("widget"); // PERMISSION_DENIED: does not have children color and size ref.setValue("foo"); // PERMISSION DENIED: does not have child color ref.child("size").setValue(22); // PERMISSION_DENIED: size is not a number Map<String,Object> map = new HashMap<String, Object>(); map.put("size","foo"); map.put("color","red"); ref.setValue(map); // SUCCESS (assuming 'blue' appears in our colors list) map = new HashMap<String, Object>(); map.put("size", 21); map.put("color","blue"); ref.setValue(map); // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child("size").setValue(99);
REST
# PERMISSION_DENIED: does not have children color and size
curl -X PUT -d 'foo' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json
# PERMISSION DENIED: does not have child color
curl -X PUT -d '{"size": 22}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json
# PERMISSION_DENIED: size is not a number
curl -X PUT -d '{"size": "foo", "color": "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json
# SUCCESS (assuming 'blue' appears in our colors list)
curl -X PUT -d '{"size": 21, "color": "blue"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json
# If the record already exists and has a color, this will
# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
# will fail to validate
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.jsonตอนนี้มาดูโครงสร้างเดียวกัน แต่ใช้กฎ .write แทนกฎ .validate
{ "rules": { // this variant will NOT allow deleting records (since .write would be disallowed) "widget": { // a widget must have 'color' and 'size' in order to be written to this path ".write": "newData.hasChildren(['color', 'size'])", "size": { // the value of "size" must be a number between 0 and 99, ONLY IF WE WRITE DIRECTLY TO SIZE ".write": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99" }, "color": { // the value of "color" must exist as a key in our mythical valid_colors/ index // BUT ONLY IF WE WRITE DIRECTLY TO COLOR ".write": "root.child('valid_colors/'+newData.val()).exists()" } } } }
ในตัวแปรนี้ การดำเนินการใดๆ ต่อไปนี้จะสำเร็จ
JavaScript
var ref = new Firebase(URL + "/widget"); // ALLOWED? Even though size is invalid, widget has children color and size, // so write is allowed and the .write rule under color is ignored ref.set({size: 99999, color: 'red'}); // ALLOWED? Works even if widget does not exist, allowing us to create a widget // which is invalid and does not have a valid color. // (allowed by the write rule under "color") ref.child('size').set(99);
Objective-C
Firebase *ref = [[Firebase alloc] initWithUrl:URL]; // ALLOWED? Even though size is invalid, widget has children color and size, // so write is allowed and the .write rule under color is ignored [ref setValue: @{ @"size": @9999, @"color": @"red" }]; // ALLOWED? Works even if widget does not exist, allowing us to create a widget // which is invalid and does not have a valid color. // (allowed by the write rule under "color") [[ref childByAppendingPath:@"size"] setValue: @99];
Swift
var ref = Firebase(url:URL) // ALLOWED? Even though size is invalid, widget has children color and size, // so write is allowed and the .write rule under color is ignored ref.setValue(["size": 9999, "color": "red"]) // ALLOWED? Works even if widget does not exist, allowing us to create a widget // which is invalid and does not have a valid color. // (allowed by the write rule under "color") ref.childByAppendingPath("size").setValue(99)
Java
Firebase ref = new Firebase(URL + "/widget"); // ALLOWED? Even though size is invalid, widget has children color and size, // so write is allowed and the .write rule under color is ignored Map<String,Object> map = new HashMap<String, Object>(); map.put("size", 99999); map.put("color", "red"); ref.setValue(map); // ALLOWED? Works even if widget does not exist, allowing us to create a widget // which is invalid and does not have a valid color. // (allowed by the write rule under "color") ref.child("size").setValue(99);
REST
# ALLOWED? Even though size is invalid, widget has children color and size,
# so write is allowed and the .write rule under color is ignored
curl -X PUT -d '{size: 99999, color: "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json
# ALLOWED? Works even if widget does not exist, allowing us to create a widget
# which is invalid and does not have a valid color.
# (allowed by the write rule under "color")
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json
ซึ่งแสดงให้เห็นความแตกต่างระหว่างกฎ .write และกฎ .validate
ตามที่แสดงไว้ กฎทั้งหมดนี้ควรเขียนโดยใช้ .validate ยกเว้นกฎ newData.hasChildren() ซึ่งจะขึ้นอยู่กับว่าควรอนุญาตการลบหรือไม่
กฎที่อิงตามการค้นหา
แม้ว่าคุณจะใช้กฎเป็นตัวกรองไม่ได้ แต่คุณ
สามารถจำกัดการเข้าถึงข้อมูลบางส่วนได้โดยใช้พารามิเตอร์การค้นหาในกฎ
ใช้การแสดงผล query. ในกฎเพื่อให้สิทธิ์อ่านหรือเขียนตามพารามิเตอร์การค้นหา
ตัวอย่างเช่น กฎที่อิงตามการค้นหาต่อไปนี้ใช้ กฎการรักษาความปลอดภัยที่อิงตามผู้ใช้
และกฎที่อิงตามการค้นหาเพื่อจำกัดการเข้าถึงข้อมูลในคอลเล็กชัน baskets
ให้เฉพาะตะกร้าสินค้าที่เป็นของผู้ใช้ที่ใช้งานอยู่เท่านั้น
"baskets": {
".read": "auth.uid !== null &&
query.orderByChild === 'owner' &&
query.equalTo === auth.uid" // restrict basket access to owner of basket
}
การค้นหาต่อไปนี้ซึ่งรวมพารามิเตอร์การค้นหาไว้ในกฎจะสำเร็จ
db.ref("baskets").orderByChild("owner")
.equalTo(auth.currentUser.uid)
.on("value", cb) // Would succeed
อย่างไรก็ตาม การค้นหาที่ไม่มีพารามิเตอร์ในกฎจะล้มเหลวโดยมีข้อผิดพลาด PermissionDenied
db.ref("baskets").on("value", cb) // Would fail with PermissionDenied
นอกจากนี้ คุณยังใช้กฎที่อิงตามการค้นหาเพื่อจำกัดปริมาณข้อมูลที่ไคลเอ็นต์ดาวน์โหลดผ่านการดำเนินการอ่านได้ด้วย
ตัวอย่างเช่น กฎต่อไปนี้จำกัดสิทธิ์อ่านให้เฉพาะผลลัพธ์ 1,000 รายการแรกของการค้นหาตามลำดับความสำคัญ
messages: {
".read": "query.orderByKey &&
query.limitToFirst <= 1000"
}
// Example queries:
db.ref("messages").on("value", cb) // Would fail with PermissionDenied
db.ref("messages").limitToFirst(1000)
.on("value", cb) // Would succeed (default order by key)
นิพจน์ query. ต่อไปนี้พร้อมใช้งานในกฎการรักษาความปลอดภัยของ Realtime Database
| การแสดงผลกฎที่อิงตามการค้นหา | ||
|---|---|---|
| นิพจน์ | ประเภท | คำอธิบาย |
| query.orderByKey query.orderByPriority query.orderByValue |
boolean | เป็นจริงสำหรับการค้นหาที่จัดเรียงตามคีย์ ลำดับความสำคัญ หรือค่า เป็นเท็จในกรณีอื่นๆ |
| query.orderByChild | string null |
ใช้สตริงเพื่อแสดงเส้นทางสัมพัทธ์ไปยังโหนดย่อย เช่น
query.orderByChild === "address/zip". หากการค้นหาไม่ได้
จัดเรียงตามโหนดย่อย ค่านี้จะเป็นค่าว่าง
|
| query.startAt query.endAt query.equalTo |
string number boolean null |
ดึงข้อมูลขอบเขตของการค้นหาที่ดำเนินการ หรือแสดงค่าว่างหากไม่มีการตั้งค่าขอบเขต |
| query.limitToFirst query.limitToLast |
number null |
ดึงข้อมูลขีดจำกัดของการค้นหาที่ดำเนินการ หรือแสดงค่าว่างหากไม่มี การตั้งค่าขีดจำกัด |
ขั้นตอนถัดไป
หลังจากที่ได้พูดคุยเกี่ยวกับเงื่อนไขแล้ว คุณจะมีความเข้าใจที่ซับซ้อนมากขึ้นเกี่ยวกับ Security Rulesและพร้อมที่จะทำสิ่งต่อไปนี้
ดูวิธีจัดการกรณีการใช้งานหลัก และดูเวิร์กโฟลว์สำหรับการพัฒนา ทดสอบ และใช้ Security Rules:
- ดูข้อมูลเกี่ยวกับตัวแปรทั้งหมดที่กำหนดไว้ล่วงหน้าของ Security Rules ที่คุณใช้ สร้างเงื่อนไขได้
- เขียนกฎที่จัดการกับสถานการณ์ทั่วไป
- ต่อยอดความรู้โดยการตรวจสอบสถานการณ์ที่คุณต้องระบุและหลีกเลี่ยงกฎที่ไม่ปลอดภัย
- ดูข้อมูลเกี่ยวกับ ชุดโปรแกรมจำลองภายในของ Firebase และวิธีใช้เพื่อ ทดสอบ Security Rules
- ตรวจสอบวิธีการที่ใช้ได้สำหรับการใช้Security Rules
ดูฟีเจอร์ที่เจาะจงสำหรับ Realtime Database ของ Security Rules: