รู้จัก Security Rules ใน Firebase Realtime Database

Jirawatee
Jirawatee
Oct 16, 2016 · 6 min read

การกำหนดค่า Security Rules

Simulator สำหรับการทดสอบ Security Rulesใน Firebase Realtime Database
รายละเอียดการทดสอบ Security Rules ใน Firebase Realtime Database

การจำกัดการเข้าถึง

ตัวอย่างการจำกัดการเข้าถึงแบบต่าง

{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
{
"rules": {
".read": true,
".write": true
}
}
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
{
"rules": {
".read": false,
".write": false
}
}

ประเภทของ Rules

".read": "auth != null && auth.provider == 'facebook'"
".write": "auth != null && auth.isAdmin == true"
".validate": "newData.hasChildren(['name', 'age'])"
{
"rules": {
"dinosaurs": {
".indexOn": ["height", "length"]
}
}
}

ตัวแปรที่ฟ้ากำหนดมา (Predefined Variables)


โครงสร้างของ Rules (Structuring Your Rules)

{
"messages": {
"message0": {
"content": "Hello",
"timestamp": 1405704370369
},
"message1": {
"content": "Goodbye",
"timestamp": 1405704395231
}
}
}
{
"rules": {
"messages": {
"$message": {
//only data from the last ten minutes can be read
".read": "data.child('timestamp').val() > (now - 600000)",
".write": "auth != null",//new data must have a string content and a number timestamp
".validate": "newData.hasChildren(['content', 'timestamp'])
&& newData.child('content').isString()
&& newData.child('content').val().length < 100
&& newData.child('timestamp').isNumber()"
}
}
}
}

การอ้างถึงข้อมูลใน path อื่นๆ (Referencing Data in other Paths)

".write": "root.child('allow_writes').val() === true &&
!data.parent().child('readOnly').exists() &&
newData.child('foo').exists()"

การสืบทอดคุณสมบัติ Read และ Write (Read and Write Rules Cascade)


การตรวจสอบข้อมูล (Validating Data)

ตัวอย่างแรก

{
"rules": {
// ให้สิทธิ์ทุกคน อ่าน, เขียน และลบได้ (ทุก path)
".read": true,
".write": true,
// ค่าของ birthdate จะต้องอยู่ในรูปแบบ YYYY-MM-DD ตั้งแต่ปี 1900–2099
"birthdate": {
".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])$/)"
}
}
}
ปีเกิน 2099 ก็ไม่ผ่านนะจ๊ะ

ตัวอย่างที่สอง ใช้ .validate กำกับตาม path

{
"rules": {
// ต้องผ่านการ authen มาก่อนจึงจะ อ่าน, เขียน และลบได้ (ทุก path)
".read": "auth != null",
".write": "auth != null",
"widget": {
// จะต้องมี attributes "color" และ "size"
".validate": "newData.hasChildren(['color', 'size'])",
"size": {
// ค่าของ "size" จะต้องเป็นเลขที่มีค่าตั้งแต่ 0 ถึง 99
".validate": "newData.isNumber() &&
newData.val() >= 0 &&
newData.val() <= 99"
},
"color": {
// ค่าของ "color" มีใน child "/valid_colors/"
".validate": "root.child('valid_colors/' +
newData.val()).exists()"
}
}
}
}
ไม่ผ่านเพราะติด .validate ชุดที่สองที่ระบุว่าค่าสีจะต้องมีใน valiad_colors (ถึงแม้จะผ่าน .validate ชุดแรกก็ตาม)
ไม่ผ่านเพราะติด .validate ชุดแรกที่บอกว่าข้อมูลจะต้องมี 2 attributes คือ color และ size
ต้องผ่าน .validate ครบทุกตัว การ write จึงจะสำเร็จ (มีสี blue ใน node ชื่อ valid_colors แล้ว)

ตัวอย่างสุดท้าย ใช้ .write ลูกเดียว ไม่มี .validate

{
"rules": {
// จะลบ widget ไม่ได้นะ เพราะไม่ได้ประกาศ .write ไว้ที่ root
"widget": {
// จะต้องมี attributes "color" และ "size"
".write": "newData.hasChildren(['color', 'size'])",
"size": {
// ค่าของ "size" จะต้องเป็นเลขที่มีค่าตั้งแต่ 0 ถึง 99
// เฉพาะการเขียนที่เจาะจงมาที่ size จึงจะทำงาน
".write": "newData.isNumber() &&
newData.val() >= 0 && newData.val() <= 99"
},
"color": {
// ค่าของ "color" มีใน child "/valid_colors/"
// เฉพาะการเขียนที่เจาะจงมาที่ color จึงจะทำงาน
".write": "root.child('valid_colors/' +
newData.val()).exists()"
}
}
}
}
ผ่านฉลุยซะงั้นถึงแม้จะไม่ระบุ color และ size เกิน 99 นั่นก็เพราะ .write มันสืบทอดคุณสมบัติจาก parent ที่บอกว่าแค่มี attributes 2 ตัว คือ color และ size ก็ผ่าน
ระบุไปที่ path ชื่อ size ตรงๆเลย แล้วใส่ 99 ปรากฏว่า write ได้นะจ๊ะ เพราะเงื่อนไขของการ write ถูกคิดจาก path ตรงที่ชี้ไป

การสร้าง rulesให้กับ path ที่เป็น dynamic (wildcard)

{
"rules": {
"rooms": {
// id ของห้องทุกห้อง
"$room_id": {
"topic": {
// ชื่อห้องจะสามารถเปลี่ยนได้ถ้า id ห้องมีคำว่า "public"
".write": "$room_id.contains('public')"
}
}
}
}
}
{
"rules": {
"widget": {
// ข้อมูลต้องมี attribute ชื่อ title และ color
"title": { ".validate": true },
"color": { ".validate": true },

// attribute ตัวอื่นเข้ามา ไม่ผ่านนะ
"$other": { ".validate": false }
}
}
}

การทำ Index

1. การ index กับ orderByChild

database ของ dinosaurs
{
"rules": {
"dinosaurs": {
".indexOn": ["height", "length"]
}
}
}

2. การ index กับ orderByValue

{
"scores": {
"ManUtd" : 60,
"Liverpool" : 48,
"Arsenal" : 54,
"Chelsea" : 45,
"ManCity" : 57,
"Everton" : 51
}
}
{
"rules": {
"scores": {
".indexOn": ".value"
}
}
}

Firebase Thailand

Let you learn and share your Firebase experiences with each other.

Jirawatee

Written by

Jirawatee

Technology Evangelist at LINE Thailand / Google Developer Expert - Firebase

Firebase Thailand

Let you learn and share your Firebase experiences with each other.