ใช้เงื่อนไขในกฎการรักษาความปลอดภัยของ 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;
}

ส่วนตัวของกลุ่ม

กรณีการใช้งานทั่วไปอีกอย่างหนึ่งคือ การให้สิทธิ์กลุ่มในออบเจ็กต์ เช่น การอนุญาตให้สมาชิกในทีมหลายคนทำงานร่วมกันในเอกสารที่แชร์ ซึ่งทำได้หลายวิธีดังนี้

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

// 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 แมป<สตริง, สตริง> ค่าทรัพยากรใหม่ แสดงเฉพาะในคำขอ 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 กี่รายการก็ได้ แต่ต้องสิ้นสุดด้วยคำสั่งส่งกลับ

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

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

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

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