ใช้เงื่อนไขในกฎการรักษาความปลอดภัยของ Firebase Cloud Storage

คู่มือนี้ต่อยอดจากคู่มือเรียนรู้ไวยากรณ์หลักของภาษา 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 อีกอย่างหนึ่งที่พบบ่อยพอๆ กันคือการอนุญาตให้กลุ่มมีสิทธิ์ในออบเจ็กต์ เช่น อนุญาตให้สมาชิกในทีมหลายคนทำงานร่วมกันในเอกสารที่แชร์ ซึ่งทำได้หลายวิธีดังนี้

เมื่อจัดเก็บข้อมูลนี้ไว้ในข้อมูลเมตาของโทเค็นหรือไฟล์แล้ว คุณจะอ้างอิงข้อมูลดังกล่าวได้จากภายในกฎ

// 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 จะช่วยให้ดูแลรักษาได้ง่ายขึ้นเมื่อกฎมีความซับซ้อนมากขึ้น

ขั้นตอนถัดไป

หลังจากการพูดคุยเกี่ยวกับเงื่อนไขนี้ คุณจะมีความเข้าใจกฎที่ซับซ้อนมากขึ้นและพร้อมที่จะทําสิ่งต่อไปนี้

ดูวิธีจัดการกรณีการใช้งานหลัก และดูเวิร์กโฟลว์สำหรับการพัฒนา การทดสอบ และการใช้กฎ