คู่มือนี้ต่อยอดจากคู่มือการเรียนรู้ไวยากรณ์หลักของภาษาFirebase Security Rules guide เพื่อแสดงวิธีเพิ่มเงื่อนไขลงในFirebase Security RulesสำหรับCloud Storage
องค์ประกอบพื้นฐานหลักของ Cloud Storage Security Rules คือ เงื่อนไข เงื่อนไขคือนิพจน์บูลีนที่กำหนดว่าควรอนุญาตหรือปฏิเสธการดำเนินการหนึ่งๆ สำหรับกฎพื้นฐาน การใช้ตัวอักษร true และ false เป็นเงื่อนไขนั้นใช้ได้ดี แต่Firebase Security RulesสำหรับCloud Storage
ภาษา มีวิธีเขียนเงื่อนไขที่ซับซ้อนมากขึ้นซึ่งทำสิ่งต่อไปนี้ได้
- ตรวจสอบการตรวจสอบสิทธิ์ของผู้ใช้
- ตรวจสอบข้อมูลขาเข้า
การตรวจสอบสิทธิ์
Firebase Security Rules สำหรับ Cloud Storage ทำงานร่วมกับ Firebase Authentication เพื่อให้ การตรวจสอบสิทธิ์ที่อิงตามผู้ใช้ที่มีประสิทธิภาพสูงแก่ Cloud Storage ซึ่งช่วยให้ควบคุมการเข้าถึงแบบละเอียดตามการอ้างสิทธิ์ของโทเค็น Firebase Authentication ได้
เมื่อผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์ทำการขอไปยัง Cloud Storage ระบบจะป้อนข้อมูลลงในตัวแปร request.auth ด้วย uid ของผู้ใช้ (request.auth.uid) รวมถึงการอ้างสิทธิ์ของ Firebase Authentication JWT (request.auth.token)
นอกจากนี้ เมื่อใช้การตรวจสอบสิทธิ์ที่กำหนดเอง ระบบจะแสดงการอ้างสิทธิ์เพิ่มเติมในช่อง request.auth.token
เมื่อผู้ใช้ที่ยังไม่ผ่านการตรวจสอบสิทธิ์ทำการขอ ตัวแปร request.auth จะเป็น null
การใช้ข้อมูลนี้มีวิธีทั่วไปหลายวิธีในการใช้การตรวจสอบสิทธิ์เพื่อรักษาความปลอดภัยของไฟล์ ดังนี้
- สาธารณะ: ละเว้น
request.auth - ส่วนตัวที่ผ่านการตรวจสอบสิทธิ์: ตรวจสอบว่า
request.authไม่ใช่null - ส่วนตัวของผู้ใช้: ตรวจสอบว่า
request.auth.uidเท่ากับuidของเส้นทาง - ส่วนตัวของกลุ่ม: ตรวจสอบการอ้างสิทธิ์ของโทเค็นที่กำหนดเองให้ตรงกับการอ้างสิทธิ์ที่เลือก หรืออ่านข้อมูลเมตาของไฟล์เพื่อดูว่ามีช่องข้อมูลเมตาอยู่หรือไม่
สาธารณะ
กฎใดก็ตามที่ไม่ได้พิจารณาบริบท request.auth ถือเป็นกฎ
public เนื่องจากไม่ได้พิจารณาบริบทการตรวจสอบสิทธิ์ของผู้ใช้
กฎเหล่านี้มีประโยชน์สำหรับการแสดงข้อมูลสาธารณะ เช่น ชิ้นงานเกม ไฟล์เสียง หรือเนื้อหาคงที่อื่นๆ
// Anyone to read a public image if the file is less than 100kB // Anyone can upload a public file ending in '.txt' match /public/{imageId} { allow read: if resource.size < 100 * 1024; allow write: if imageId.matches(".*\\.txt"); }
ส่วนตัวที่ผ่านการตรวจสอบสิทธิ์
ในบางกรณี คุณอาจต้องการให้ผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์ทั้งหมดของแอปพลิเคชันดูข้อมูลได้ แต่ผู้ใช้ที่ยังไม่ผ่านการตรวจสอบสิทธิ์จะดูไม่ได้ เนื่องจากตัวแปร request.auth เป็น null สำหรับผู้ใช้ที่ยังไม่ผ่านการตรวจสอบสิทธิ์ทั้งหมด สิ่งที่คุณต้องทำคือตรวจสอบว่าตัวแปร request.auth มีอยู่หรือไม่เพื่อกำหนดให้ต้องมีการตรวจสอบสิทธิ์
// Require authentication on all internal image reads match /internal/{imageId} { allow read: if request.auth != null; }
ส่วนตัวของผู้ใช้
กรณีการใช้งาน request.auth ที่พบมากที่สุดคือการให้สิทธิ์แบบละเอียดแก่ผู้ใช้แต่ละรายในไฟล์ของตนเอง ตั้งแต่การอัปโหลดรูปโปรไฟล์ไปจนถึงการอ่านเอกสารส่วนตัว
เนื่องจากไฟล์ใน Cloud Storage มี "เส้นทาง" แบบเต็มไปยังไฟล์ สิ่งที่คุณต้องทำ
เพื่อให้ผู้ใช้ควบคุมไฟล์ได้คือข้อมูลที่ไม่ซ้ำกันซึ่งระบุตัวตนของผู้ใช้
ในคำนำหน้าชื่อไฟล์ (เช่น uid ของผู้ใช้) ซึ่งสามารถ
ตรวจสอบได้เมื่อมีการประเมินกฎ:
// Only a user can upload their profile picture, but anyone can view it match /users/{userId}/profilePicture.png { allow read; allow write: if request.auth.uid == userId; }
ส่วนตัวของกลุ่ม
กรณีการใช้งานที่พบมากอีกกรณีหนึ่งคือการอนุญาตสิทธิ์ของกลุ่มในออบเจ็กต์ เช่น การอนุญาตให้สมาชิกในทีมหลายคนทำงานร่วมกันในเอกสารที่แชร์ ซึ่งทำได้หลายวิธี ดังนี้
- สร้างโทเค็นที่กำหนดเองของ Firebase Authentication Firebase Authentication ซึ่งมีข้อมูลเพิ่มเติมเกี่ยวกับสมาชิกในกลุ่ม (เช่น รหัสกลุ่ม)
- ใส่ข้อมูลกลุ่ม (เช่น รหัสกลุ่มหรือรายการ
uidที่ได้รับอนุญาต) ใน ข้อมูลเมตาของ ไฟล์
เมื่อจัดเก็บข้อมูลนี้ไว้ในโทเค็นหรือข้อมูลเมตาของไฟล์แล้ว คุณจะอ้างอิงข้อมูลนี้จากภายในกฎได้
// Allow reads if the group ID in your token matches the file metadata's `owner` property // Allow writes if the group ID is in the user's custom token match /files/{groupId}/{fileName} { allow read: if resource.metadata.owner == request.auth.token.groupId; allow write: if request.auth.token.groupId == groupId; }
การประเมินคำขอ
ระบบจะประเมินการอัปโหลด การดาวน์โหลด การเปลี่ยนแปลงข้อมูลเมตา และการลบโดยใช้
request ที่ส่งไปยัง Cloud Storage นอกจากรหัสที่ไม่ซ้ำกันของผู้ใช้และ
เพย์โหลด Firebase Authentication ในออบเจ็กต์ request.auth ตามที่อธิบายไว้ข้างต้นแล้ว
ตัวแปร request ยังมีเส้นทางไฟล์ที่กำลังดำเนินการคำขอ
เวลาที่ได้รับคำขอ และค่า resource ใหม่
หากคำขอเป็นการเขียน
ออบเจ็กต์ request ยังมีรหัสที่ไม่ซ้ำกันของผู้ใช้และ
Firebase Authentication เพย์โหลดในออบเจ็กต์ request.auth ซึ่งจะ
อธิบายเพิ่มเติมในส่วนการรักษาความปลอดภัยที่อิงตามผู้ใช้
ของเอกสาร
รายการพร็อพเพอร์ตี้ทั้งหมดในออบเจ็กต์ request มีดังนี้
| พร็อพเพอร์ตี้ | ประเภท | คำอธิบาย |
|---|---|---|
auth |
map<string, string> | เมื่อผู้ใช้เข้าสู่ระบบ จะระบุ uid ซึ่งเป็นรหัสที่ไม่ซ้ำกันของผู้ใช้ และ
token ซึ่งเป็นแผนที่ของการอ้างสิทธิ์ JWT Firebase Authentication หากไม่เป็นเช่นนั้น ค่าจะเป็น
null |
params |
map<string, string> | แผนที่ที่มีพารามิเตอร์การค้นหาของคำขอ |
path |
path | A path ที่แสดงเส้นทางที่กำลังดำเนินการคำขอ
|
resource |
map<string, string> | ค่าทรัพยากรใหม่ ซึ่งมีเฉพาะในคำขอ write
|
time |
timestamp | การประทับเวลาที่แสดงเวลาเซิร์ฟเวอร์ที่ประเมินคำขอ |
การประเมินทรัพยากร
เมื่อประเมินกฎ คุณอาจต้องการประเมินข้อมูลเมตาของไฟล์ที่กำลังอัปโหลด ดาวน์โหลด แก้ไข หรือลบด้วย ซึ่งจะช่วยให้คุณสร้างกฎที่ซับซ้อนและมีประสิทธิภาพซึ่งทำสิ่งต่างๆ ได้ เช่น อนุญาตให้อัปโหลดเฉพาะไฟล์ที่มีประเภทเนื้อหาบางประเภท หรืออนุญาตให้ลบเฉพาะไฟล์ที่มีขนาดมากกว่าขนาดที่กำหนด
Firebase Security Rules สำหรับ Cloud Storage มีข้อมูลเมตาของไฟล์ในออบเจ็กต์ resource
ซึ่งมีคู่คีย์/ค่าของข้อมูลเมตาที่แสดงในออบเจ็กต์
Cloud Storage คุณสามารถตรวจสอบพร็อพเพอร์ตี้เหล่านี้ในคำขอ read หรือ write เพื่อให้มั่นใจในความสมบูรณ์ของข้อมูล
ในคำขอ write (เช่น การอัปโหลด การอัปเดตข้อมูลเมตา และการลบ) นอกเหนือจากออบเจ็กต์ resource ซึ่งมีข้อมูลเมตาของไฟล์สำหรับไฟล์ที่อยู่ในเส้นทางคำขอในปัจจุบันแล้ว คุณยังสามารถใช้ออบเจ็กต์ request.resource ซึ่งมีข้อมูลเมตาของไฟล์ที่เป็นส่วนย่อยที่จะเขียนหากได้รับอนุญาตให้เขียน คุณสามารถใช้ค่าทั้ง 2 นี้เพื่อให้มั่นใจในความสมบูรณ์ของข้อมูลหรือบังคับใช้ข้อจำกัดของแอปพลิเคชัน เช่น ประเภทหรือขนาดของไฟล์
รายการพร็อพเพอร์ตี้ทั้งหมดในออบเจ็กต์ resource มีดังนี้
| พร็อพเพอร์ตี้ | ประเภท | คำอธิบาย |
|---|---|---|
name |
string | ชื่อเต็มของออบเจ็กต์ |
bucket |
string | ชื่อบัคเก็ตที่ออบเจ็กต์นี้อยู่ |
generation |
int | รุ่นออบเจ็กต์ Google Cloud Storage ของออบเจ็กต์นี้ |
metageneration |
int | รุ่นข้อมูลเมตาของออบเจ็กต์ Google Cloud Storage ของออบเจ็กต์นี้ |
size |
int | ขนาดของออบเจ็กต์ในหน่วยไบต์ |
timeCreated |
timestamp | การประทับเวลาที่แสดงเวลาที่สร้างออบเจ็กต์ |
updated |
timestamp | การประทับเวลาที่แสดงเวลาที่อัปเดตออบเจ็กต์ครั้งล่าสุด |
md5Hash |
string | แฮช MD5 ของออบเจ็กต์ |
crc32c |
string | แฮช crc32c ของออบเจ็กต์ |
etag |
string | etag ที่เชื่อมโยงกับออบเจ็กต์นี้ |
contentDisposition |
string | การจัดวางเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้ |
contentEncoding |
string | การเข้ารหัสเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้ |
contentLanguage |
string | ภาษาของเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้ |
contentType |
string | ประเภทเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้ |
metadata |
map<string, string> | คู่คีย์/ค่าของข้อมูลเมตาที่กำหนดเองเพิ่มเติมที่นักพัฒนาซอฟต์แวร์ระบุ |
request.resource มีข้อมูลทั้งหมดนี้ ยกเว้น generation,
metageneration, etag, timeCreated และ updated
ปรับปรุงด้วย Cloud Firestore
คุณสามารถเข้าถึงเอกสารใน Cloud Firestore เพื่อประเมินเกณฑ์การให้สิทธิ์อื่นๆ ได้
การใช้ฟังก์ชัน firestore.get() และ firestore.exists() กฎการรักษาความปลอดภัย
จะประเมินคำขอขาเข้าเทียบกับเอกสารใน Cloud Firestore ได้
ฟังก์ชัน firestore.get() และ firestore.exists() ทั้ง 2 ฟังก์ชันคาดหวังเส้นทางเอกสารที่ระบุไว้อย่างครบถ้วน เมื่อใช้ตัวแปรเพื่อสร้างเส้นทางสำหรับ firestore.get() และ firestore.exists() คุณต้องหลีกตัวแปรอย่างชัดแจ้งโดยใช้ไวยากรณ์ $(variable)
ในตัวอย่างด้านล่าง เราจะเห็นกฎที่จำกัดสิทธิ์อ่านไฟล์ไว้สำหรับผู้ใช้ที่เป็นสมาชิกของคลับบางคลับเท่านั้น
service firebase.storage {
match /b/{bucket}/o {
match /users/{club}/files/{fileId} {
allow read: if club in
firestore.get(/databases/(default)/documents/users/$(request.auth.id)).data.memberships
}
}
}service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/photos/{fileId} {
allow read: if
firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id))
}
}
}เมื่อสร้างและบันทึก Cloud Storage Security Rules แรกที่ใช้ Cloud Firestore ฟังก์ชันเหล่านี้ ระบบจะแจ้งให้คุณเปิดใช้สิทธิ์ในการเชื่อมต่อผลิตภัณฑ์ทั้ง 2 รายการในคอนโซล Firebase หรือ Firebase CLI
คุณสามารถปิดใช้ฟีเจอร์นี้ได้โดยนำบทบาท IAM ออกตามที่อธิบายไว้ใน จัดการและติดตั้งใช้งาน Firebase Security Rules
ตรวจสอบข้อมูล
Firebase Security Rules สำหรับ Cloud Storage ยังใช้เพื่อตรวจสอบข้อมูลได้ด้วย ซึ่งรวมถึง
การตรวจสอบชื่อและเส้นทางของไฟล์ รวมถึงพร็อพเพอร์ตี้ข้อมูลเมตาของไฟล์ เช่น
contentType และ size
service firebase.storage { match /b/{bucket}/o { match /images/{imageId} { // Only allow uploads of any image file that's less than 5MB allow write: if request.resource.size < 5 * 1024 * 1024 && request.resource.contentType.matches('image/.*'); } } }
ฟังก์ชันที่กำหนดเอง
เมื่อ Firebase Security Rules ซับซ้อนมากขึ้น คุณอาจต้องการรวมชุด เงื่อนไขไว้ในฟังก์ชันที่คุณนำไปใช้ซ้ำในชุดกฎได้ กฎการรักษาความปลอดภัยรองรับฟังก์ชันที่กำหนดเอง ไวยากรณ์ของฟังก์ชันที่กำหนดเองจะคล้ายกับ JavaScript แต่ Firebase Security Rules ฟังก์ชันเขียนด้วยภาษาเฉพาะโดเมน ซึ่งมีข้อจำกัดที่สำคัญบางประการ ดังนี้
- ฟังก์ชันจะมีคำสั่ง
returnเพียงคำสั่งเดียวเท่านั้น และไม่มีตรรกะเพิ่มเติม เช่น ฟังก์ชันจะวนซ้ำหรือเรียกใช้บริการภายนอกไม่ได้ - ฟังก์ชันจะเข้าถึงฟังก์ชันและตัวแปรจากขอบเขตที่กำหนดไว้ได้โดยอัตโนมัติ เช่น ฟังก์ชันที่กำหนดไว้ใน
ขอบเขต
service firebase.storageจะเข้าถึงตัวแปรresourceได้ และสำหรับ Cloud Firestore เท่านั้น ฟังก์ชันในตัว เช่นget()และexists() - ฟังก์ชันอาจเรียกใช้ฟังก์ชันอื่นๆ ได้ แต่จะเรียกใช้ตัวเองซ้ำไม่ได้ ความลึกของสแต็กการเรียกใช้ทั้งหมดจำกัดไว้ที่ 10
- ในเวอร์ชัน
rules2ฟังก์ชันจะกำหนดตัวแปรได้โดยใช้คีย์เวิร์ดletฟังก์ชันจะมี Binding `let` ได้ไม่จำกัดจำนวน แต่ต้องลงท้ายด้วยคำสั่ง `return`
ฟังก์ชันจะกำหนดด้วยคีย์เวิร์ด function และรับอาร์กิวเมนต์ได้ตั้งแต่ 0 รายการขึ้นไป เช่น คุณอาจต้องการรวมเงื่อนไข 2 ประเภทที่ใช้ในตัวอย่างข้างต้นไว้ในฟังก์ชันเดียว ดังนี้
service firebase.storage {
match /b/{bucket}/o {
// True if the user is signed in or the requested data is 'public'
function signedInOrPublic() {
return request.auth.uid != null || resource.data.visibility == 'public';
}
match /images/{imageId} {
allow read, write: if signedInOrPublic();
}
match /mp3s/{mp3Ids} {
allow read: if signedInOrPublic();
}
}
}
การใช้ฟังก์ชันใน Firebase Security Rules จะช่วยให้ดูแลรักษากฎได้ง่ายขึ้นเมื่อกฎมีความซับซ้อน มากขึ้น
ขั้นตอนถัดไป
หลังจากที่ได้พูดคุยเกี่ยวกับเงื่อนไขแล้ว คุณจะมีความเข้าใจเกี่ยวกับกฎที่ซับซ้อนมากขึ้นและพร้อมที่จะทำสิ่งต่อไปนี้
ดูวิธีจัดการกรณีการใช้งานหลัก และดูเวิร์กโฟลว์สำหรับการพัฒนา การทดสอบ และการติดตั้งใช้งานกฎ
- เขียนกฎที่จัดการกับสถานการณ์ทั่วไป
- ต่อยอดความรู้โดยดูสถานการณ์ที่คุณต้องระบุกฎที่ไม่ปลอดภัยและหลีกเลี่ยงการใช้กฎดังกล่าว
- ทดสอบกฎโดยใช้โปรแกรมจำลอง Cloud Storage และไลบรารีทดสอบกฎการรักษาความปลอดภัยโดยเฉพาะ
- ดูวิธีการที่มีสำหรับการติดตั้งใช้งานSecurity Rules