คู่มือนี้ต่อยอดจากคู่มือเรียนรู้ไวยากรณ์หลักของภาษา Firebase Security Rules เพื่อแสดงวิธีเพิ่มเงื่อนไขลงใน Firebase Security Rules สำหรับ Cloud Storage
องค์ประกอบพื้นฐานหลักของ Cloud Storage Security Rules คือเงื่อนไข เงื่อนไขคือนิพจน์บูลีนที่กำหนดว่าควรอนุญาตหรือปฏิเสธการดำเนินการหนึ่งๆ สําหรับกฎพื้นฐาน การใช้นิพจน์ true
และ false
เป็นเงื่อนไขจะทํางานได้อย่างสมบูรณ์ แต่รูปแบบ Firebase Security Rules for 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; }
ผู้ใช้เท่านั้น
Use Case ที่พบบ่อยที่สุดสำหรับ 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; }
ส่วนตัวของกลุ่ม
Use Case อีกอย่างหนึ่งที่พบบ่อยพอๆ กันคือการอนุญาตให้กลุ่มมีสิทธิ์ในออบเจ็กต์ เช่น อนุญาตให้สมาชิกในทีมหลายคนทำงานร่วมกันในเอกสารที่แชร์ ซึ่งทำได้หลายวิธีดังนี้
- มินต์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 ที่แสดงเส้นทางที่ดำเนินการตามคำขอ |
resource |
map<string, string> | ค่าทรัพยากรใหม่ที่มีอยู่ในคําขอ write เท่านั้น
|
time |
การประทับเวลา | การประทับเวลาที่แสดงเวลาเซิร์ฟเวอร์ที่ประเมินคําขอ |
การประเมินทรัพยากร
เมื่อประเมินกฎ คุณอาจต้องประเมินข้อมูลเมตาของไฟล์ที่อัปโหลด ดาวน์โหลด แก้ไข หรือลบด้วย ซึ่งจะช่วยให้คุณสร้างกฎที่ซับซ้อนและมีประสิทธิภาพได้ เช่น อนุญาตให้อัปโหลดเฉพาะไฟล์ที่มีประเภทเนื้อหาบางอย่าง หรือลบเฉพาะไฟล์ที่มีขนาดใหญ่กว่าขนาดที่กำหนด
Firebase Security Rules สำหรับ Cloud Storage ให้ข้อมูลเมตาของไฟล์ในออบเจ็กต์ resource
ซึ่งมีคู่คีย์/ค่าของข้อมูลเมตาที่แสดงในออบเจ็กต์ Cloud Storage คุณสามารถตรวจสอบพร็อพเพอร์ตี้เหล่านี้ในคำขอ read
หรือ write
เพื่อให้มั่นใจว่าข้อมูลมีความสมบูรณ์
ในคำขอ write
(เช่น การอัปโหลด การอัปเดตข้อมูลเมตา และการลบ) นอกจากออบเจ็กต์ resource
ซึ่งมีข้อมูลเมตาของไฟล์สำหรับไฟล์ที่มีอยู่ในเส้นทางคำขอแล้ว คุณยังสามารถใช้ออบเจ็กต์ request.resource
ซึ่งมีข้อมูลเมตาของไฟล์ชุดย่อยที่จะเขียนได้หากระบบอนุญาตให้เขียน คุณสามารถใช้ 2 ค่านี้เพื่อรักษาความสมบูรณ์ของข้อมูลหรือบังคับใช้ข้อจำกัดของแอปพลิเคชัน เช่น ประเภทหรือขนาดไฟล์
รายการพร็อพเพอร์ตี้ทั้งหมดในแอบเจ็กต์ resource
มีดังนี้
พร็อพเพอร์ตี้ | ประเภท | คำอธิบาย |
---|---|---|
name |
สตริง | ชื่อเต็มของออบเจ็กต์ |
bucket |
สตริง | ชื่อของที่เก็บข้อมูลที่มีออบเจ็กต์นี้อยู่ |
generation |
int | Google Cloud Storage รุ่นออบเจ็กต์ของออบเจ็กต์นี้ |
metageneration |
int | Google Cloud Storage การสร้างข้อมูลเมตาของออบเจ็กต์ของออบเจ็กต์นี้ |
size |
int | ขนาดของออบเจ็กต์ในหน่วยไบต์ |
timeCreated |
การประทับเวลา | การประทับเวลาที่แสดงเวลาที่สร้างออบเจ็กต์ |
updated |
การประทับเวลา | การประทับเวลาที่แสดงเวลาที่อัปเดตออบเจ็กต์ครั้งล่าสุด |
md5Hash |
สตริง | แฮช MD5 ของออบเจ็กต์ |
crc32c |
สตริง | แฮช CRC32c ของออบเจ็กต์ |
etag |
สตริง | ETag ที่เชื่อมโยงกับออบเจ็กต์นี้ |
contentDisposition |
สตริง | การจัดการเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้ |
contentEncoding |
สตริง | การเข้ารหัสเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้ |
contentLanguage |
สตริง | ภาษาของเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้ |
contentType |
สตริง | ประเภทเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้ |
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()
ต้องการเส้นทางเอกสารที่ระบุอย่างครบถ้วน เมื่อใช้ตัวแปรเพื่อสร้างเส้นทางสําหรับ 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)).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 เหล่านี้แล้ว คุณจะได้รับข้อความแจ้งในคอนโซล Firebase หรือ Firebase CLI ให้เปิดใช้สิทธิ์เพื่อเชื่อมต่อผลิตภัณฑ์ 2 รายการ
คุณปิดใช้ฟีเจอร์นี้ได้โดยการนําบทบาท 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
และฟังก์ชันในตัว เช่นget()
และexists()
สำหรับ Cloud Firestore เท่านั้น - ฟังก์ชันอาจเรียกใช้ฟังก์ชันอื่นๆ แต่ต้องไม่เรียกซ้ำ ระดับความลึกของกองซ้อนการเรียกทั้งหมดถูกจํากัดไว้ที่ 10
- ในเวอร์ชัน
rules2
ฟังก์ชันจะกําหนดตัวแปรได้โดยใช้คีย์เวิร์ดlet
ฟังก์ชันมีการเชื่อมโยง 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 และไลบรารีทดสอบกฎการรักษาความปลอดภัยโดยเฉพาะ
- ตรวจสอบวิธีการที่ใช้ได้สําหรับการติดตั้งใช้งาน Rules