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

คู่มือนี้สร้างขึ้นจากคู่มือเรียนรู้ไวยากรณ์หลักของภาษาของกฎการรักษาความปลอดภัยของ Firebase เพื่อแสดงวิธีเพิ่มเงื่อนไขในกฎการรักษาความปลอดภัยของ Firebase สำหรับ Cloud Storage

องค์ประกอบที่ใช้สร้างสรรค์หลักของกฎความปลอดภัยของ Cloud Storage คือเงื่อนไข เงื่อนไขคือนิพจน์บูลีนที่กำหนดว่าควรอนุญาตหรือปฏิเสธการดำเนินการเฉพาะ สำหรับกฎพื้นฐาน การใช้ true และ false ลิเทอรัล เป็นเงื่อนไขที่ใช้ได้ดี แต่กฎการรักษาความปลอดภัยของ Firebase สำหรับภาษาของ Cloud Storage มอบวิธีในการเขียนเงื่อนไขที่ซับซ้อนขึ้น ซึ่งสามารถทำสิ่งต่อไปนี้

  • ตรวจสอบการตรวจสอบสิทธิ์ผู้ใช้
  • ตรวจสอบข้อมูลขาเข้า

การตรวจสอบสิทธิ์

กฎการรักษาความปลอดภัยของ Firebase สำหรับ Cloud Storage จะผสานรวมกับการตรวจสอบสิทธิ์ Firebase เพื่อมอบการตรวจสอบสิทธิ์ตามผู้ใช้ที่มีประสิทธิภาพสูงไปยัง Cloud Storage ซึ่งช่วยให้ควบคุมการเข้าถึงได้อย่างละเอียดตามการอ้างสิทธิ์โทเค็นการตรวจสอบสิทธิ์ Firebase

เมื่อผู้ใช้ที่ตรวจสอบสิทธิ์แล้วส่งคำขอกับ Cloud Storage ระบบจะป้อนข้อมูลตัวแปร request.auth ด้วย uid (request.auth.uid) ของผู้ใช้ รวมถึงการอ้างสิทธิ์ JWT การตรวจสอบสิทธิ์ของ Firebase (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 ที่มีข้อมูลเพิ่มเติมเกี่ยวกับสมาชิกกลุ่ม (เช่น รหัสกลุ่ม)
  • ใส่ข้อมูลกลุ่ม (เช่น รหัสกลุ่มหรือรายการ 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 ในออบเจ็กต์ request.auth ตามที่อธิบายไว้ข้างต้นแล้ว ตัวแปร request ยังมีเส้นทางไฟล์ที่กำลังดำเนินการ เวลาที่ได้รับคำขอ และค่า resource ใหม่หากคำขอนั้นเป็นการเขียน

นอกจากนี้ ออบเจ็กต์ request ยังมีรหัสที่ไม่ซ้ำกันของผู้ใช้และเพย์โหลดการตรวจสอบสิทธิ์ Firebase ในออบเจ็กต์ request.auth ซึ่งจะอธิบายเพิ่มเติมในส่วน User-Based Security ของเอกสาร

รายการที่พักทั้งหมดในออบเจ็กต์ request แสดงอยู่ด้านล่าง

พร็อพเพอร์ตี้ ประเภท คำอธิบาย
auth แมป<สตริง, สตริง> เมื่อผู้ใช้เข้าสู่ระบบ ให้ระบุ uid, รหัสที่ไม่ซ้ำกันของผู้ใช้ และ token ซึ่งเป็นแมปการอ้างสิทธิ์ JWT การตรวจสอบสิทธิ์ของ Firebase หากไม่ระบุ ค่าจะเป็น null
params แมป<สตริง, สตริง> แผนที่ที่มีพารามิเตอร์การค้นหาของคำขอ
path เส้นทาง path ที่แสดงเส้นทางที่กำลังดำเนินการคำขอ
resource แมป<สตริง, สตริง> ค่าทรัพยากรใหม่ แสดงเฉพาะในคำขอ write เท่านั้น
time การประทับเวลา การประทับเวลาที่แสดงถึงเวลาของเซิร์ฟเวอร์ที่มีการประเมินคำขอ

การประเมินทรัพยากร

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

กฎการรักษาความปลอดภัยของ Firebase สำหรับ 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 การประทับเวลา การประทับเวลาที่แสดงถึงเวลาที่สร้างออบเจ็กต์
updated การประทับเวลา การประทับเวลาที่แสดงถึงเวลาที่อัปเดตออบเจ็กต์ครั้งล่าสุด
md5Hash string แฮช MD5 ของออบเจ็กต์
crc32c string แฮช crc32c ของออบเจ็กต์
etag string eTag ที่เชื่อมโยงกับออบเจ็กต์นี้
contentDisposition string การจัดการเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้
contentEncoding string การเข้ารหัสเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้
contentLanguage string ภาษาของเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้
contentType string ประเภทเนื้อหาที่เชื่อมโยงกับออบเจ็กต์นี้
metadata แมป<สตริง, สตริง> คู่คีย์/ค่าของข้อมูลเมตาที่กำหนดเองเพิ่มเติมที่นักพัฒนาซอฟต์แวร์ระบุ

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

คุณปิดใช้ฟีเจอร์นี้ได้โดยการนำบทบาท IAM ออก ตามที่อธิบายในจัดการและทำให้กฎการรักษาความปลอดภัยของ Firebase ใช้งานได้

ตรวจสอบข้อมูล

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

  • ฟังก์ชันจะมีคำสั่ง return ได้เพียงคำสั่งเดียว และไม่สามารถมีตรรกะเพิ่มเติม เช่น จะไม่สามารถเรียกใช้ลูปหรือเรียกใช้บริการภายนอก
  • ฟังก์ชันจะเข้าถึงฟังก์ชันและตัวแปรจากขอบเขตที่กำหนดได้โดยอัตโนมัติ ตัวอย่างเช่น ฟังก์ชันที่กำหนดไว้ในขอบเขต service firebase.storage มีสิทธิ์เข้าถึงตัวแปร resource และสำหรับ Cloud Firestore เท่านั้น ฟังก์ชันในตัว เช่น get() และ exists()
  • ฟังก์ชันต่างๆ อาจเรียกใช้ฟังก์ชันอื่นๆ แต่อาจไม่แสดงใหม่ ความลึกของสแต็กการโทรทั้งหมดจำกัดไว้ที่ 10
  • ในเวอร์ชัน rules2 ฟังก์ชันจะกำหนดตัวแปรโดยใช้คีย์เวิร์ด let ได้ ฟังก์ชันจะมีการเชื่อมโยง Let กี่รายการก็ได้ แต่ต้องลงท้ายด้วยคำสั่งส่งกลับ

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

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

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

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