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

คู่มือนี้ต่อยอดจากคู่มือเรียนรู้ไวยากรณ์หลักของภาษา Firebase Security Rules เพื่อแสดงวิธีเพิ่มเงื่อนไขลงใน 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;
}

ผู้ใช้แบบส่วนตัว

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 ออบเจ็กต์ metageneration ของออบเจ็กต์นี้
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() ทั้ง 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 เหล่านี้แล้ว คุณจะได้รับข้อความแจ้งในคอนโซล Firebase หรือ CLI ของ Firebase ให้เปิดใช้สิทธิ์เพื่อเชื่อมต่อผลิตภัณฑ์ทั้ง 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 และสำหรับ Cloud Firestore เท่านั้น ฟังก์ชันในตัว เช่น get() และ exists()
  • ฟังก์ชันอาจเรียกฟังก์ชันอื่นๆ ได้ แต่จะเรียกตัวเองซ้ำไม่ได้ ความลึกของสแต็กการเรียกทั้งหมดจำกัดไว้ที่ 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 จะช่วยให้ดูแลรักษาได้ง่ายขึ้นเมื่อกฎมีความซับซ้อนมากขึ้น

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

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

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