Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

สืบค้นข้อมูลอย่างปลอดภัย

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

หน้านี้สร้างขึ้นจากแนวคิดใน การจัดโครงสร้างกฎความปลอดภัย และ การเขียนเงื่อนไขสำหรับกฎความปลอดภัย เพื่ออธิบายว่ากฎความปลอดภัยของ Cloud Firestore โต้ตอบกับการสืบค้นอย่างไร จะพิจารณาอย่างละเอียดถี่ถ้วนว่ากฎความปลอดภัยส่งผลต่อการสืบค้นข้อมูลที่คุณสามารถเขียนได้อย่างไร และอธิบายวิธีการตรวจสอบให้แน่ใจว่าการสืบค้นของคุณใช้ข้อจำกัดเดียวกันกับกฎความปลอดภัยของคุณ หน้านี้ยังอธิบายวิธีเขียนกฎความปลอดภัยเพื่ออนุญาตหรือปฏิเสธการสืบค้นตามคุณสมบัติการสืบค้น เช่น limit และ orderBy

กฎไม่ใช่ตัวกรอง

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

แบบสอบถามและกฎความปลอดภัย

ตามตัวอย่างด้านล่าง คุณต้องเขียนคำถามของคุณเพื่อให้เหมาะสมกับข้อจำกัดของกฎความปลอดภัยของคุณ

รักษาความปลอดภัยและสืบค้นเอกสารตาม auth.uid

ตัวอย่างต่อไปนี้สาธิตวิธีการเขียนแบบสอบถามเพื่อดึงเอกสารที่ป้องกันโดยกฎความปลอดภัย พิจารณาฐานข้อมูลที่มีชุดเอกสาร story :

/story/{storyid}

{
  title: "A Great Story",
  content: "Once upon a time...",
  author: "some_auth_id",
  published: false
}

นอกจากฟิลด์ title และ content แล้ว เอกสารแต่ละฉบับยังจัดเก็บ author และฟิลด์ที่ published เพื่อใช้สำหรับการควบคุมการเข้าถึง ตัวอย่างเหล่านี้ถือว่าแอปใช้การ รับรองความถูกต้องของ Firebase เพื่อตั้งค่าฟิลด์ author เป็น UID ของผู้ใช้ที่สร้างเอกสาร การตรวจสอบสิทธิ์ Firebase ยังเติมตัวแปร request.auth ในกฎความปลอดภัย

กฎการรักษาความปลอดภัยต่อไปนี้ใช้ตัวแปร request.auth และ resource.data เพื่อจำกัดการเข้าถึงการอ่านและเขียนสำหรับแต่ละ story ให้กับผู้เขียน:

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Only the authenticated user who authored the document can read or write
      allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

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

ไม่ถูกต้อง : ข้อจำกัดการค้นหาไม่ตรงกับข้อจำกัดของกฎความปลอดภัย

// This query will fail
db.collection("stories").get()

การสืบค้นล้มเหลว แม้ว่า ผู้ใช้ปัจจุบันจะเป็นผู้เขียนเอกสาร story ทุกฉบับจริงๆ สาเหตุของพฤติกรรมนี้คือเมื่อ Cloud Firestore ใช้กฎความปลอดภัยของคุณ ระบบจะประเมินการสืบค้นตามชุดผลลัพธ์ ที่เป็น ไปได้ ไม่ใช่กับคุณสมบัติ จริง ของเอกสารในฐานข้อมูลของคุณ ถ้าแบบสอบถาม อาจ มีเอกสารที่ละเมิดกฎความปลอดภัยของคุณ การสอบถามจะล้มเหลว

ในทางตรงกันข้าม คิวรีต่อไปนี้สำเร็จ เนื่องจากมีข้อจำกัดเดียวกันในฟิลด์ author เป็นกฎความปลอดภัย:

ถูกต้อง : ข้อ จำกัด ของแบบสอบถามตรงกับข้อ จำกัด กฎความปลอดภัย

var user = firebase.auth().currentUser;

db.collection("stories").where("author", "==", user.uid).get()

รักษาความปลอดภัยและสืบค้นเอกสารตามฟิลด์

เพื่อสาธิตการโต้ตอบระหว่างคำค้นหาและกฎเพิ่มเติม กฎความปลอดภัยด้านล่างขยายการเข้าถึงการอ่านสำหรับคอลเลกชัน stories เพื่อให้ผู้ใช้ทุกคนสามารถอ่านเอกสาร story ที่มีการตั้งค่าฟิลด์ที่ published เป็น true

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Anyone can read a published story; only story authors can read unpublished stories
      allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
      // Only story authors can write
      allow write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

แบบสอบถามสำหรับหน้าที่เผยแพร่ต้องมีข้อจำกัดเดียวกันกับกฎความปลอดภัย:

db.collection("stories").where("published", "==", true).get()

ข้อจำกัดการสืบค้น . .where("published", "==", true) รับประกันว่า resource.data.published เป็น true สำหรับผลลัพธ์ใดๆ ดังนั้น แบบสอบถามนี้จึงเป็นไปตามกฎความปลอดภัยและได้รับอนุญาตให้อ่านข้อมูลได้

in และ array-contains-any anyquestion

เมื่อประเมินส่วนคำสั่งการค้นหา in หรือ array-contains-any กับชุดกฎ Cloud Firestore จะประเมินค่าเปรียบเทียบแต่ละค่าแยกกัน ค่าเปรียบเทียบแต่ละค่าต้องเป็นไปตามข้อจำกัดของกฎความปลอดภัย ตัวอย่างเช่น สำหรับกฎต่อไปนี้:

match /mydocuments/{doc} {
  allow read: if resource.data.x > 5;
}

ไม่ถูกต้อง : แบบสอบถามไม่รับประกันว่า x > 5 สำหรับเอกสารที่เป็นไปได้ทั้งหมด

// This query will fail
db.collection("mydocuments").where("x", "in", [1, 3, 6, 42, 99]).get()

ถูกต้อง : แบบสอบถามรับประกันว่า x > 5 สำหรับเอกสารที่เป็นไปได้ทั้งหมด

db.collection("mydocuments").where("x", "in", [6, 42, 99, 105, 200]).get()

การประเมินข้อจำกัดในการสืบค้น

กฎความปลอดภัยของคุณยังสามารถยอมรับหรือปฏิเสธการสืบค้นตามข้อจำกัดของพวกเขา ตัวแปร request.query ประกอบด้วยคุณสมบัติ limit , offset และ orderBy ของเคียวรี ตัวอย่างเช่น กฎความปลอดภัยของคุณสามารถปฏิเสธการสืบค้นใดๆ ที่ไม่จำกัดจำนวนสูงสุดของเอกสารที่เรียกค้นในช่วงที่กำหนด:

allow list: if request.query.limit <= 10;

ชุดกฎต่อไปนี้สาธิตวิธีการเขียนกฎความปลอดภัยที่ประเมินข้อจำกัดที่วางไว้ในแบบสอบถาม ตัวอย่างนี้ขยายชุดกฎของ stories ก่อนหน้าโดยมีการเปลี่ยนแปลงต่อไปนี้:

  • ชุดกฎแยกกฎการอ่านออกเป็นกฎสำหรับ get และ list
  • กฎการ get จำกัดการดึงเอกสารเดี่ยวสำหรับเอกสารสาธารณะหรือเอกสารที่ผู้ใช้เขียนขึ้น
  • กฎ list ใช้ข้อจำกัดเดียวกันกับ get แต่สำหรับการสืบค้น นอกจากนี้ยังตรวจสอบขีดจำกัดการสืบค้น จากนั้นปฏิเสธการสืบค้นใดๆ ที่ไม่มีขีดจำกัดหรือขีดจำกัดที่มากกว่า 10
  • ชุดกฎกำหนด authorOrPublished() เพื่อหลีกเลี่ยงความซ้ำซ้อนของโค้ด
service cloud.firestore {

  match /databases/{database}/documents {

    match /stories/{storyid} {

      // Returns `true` if the requested story is 'published'
      // or the user authored the story
      function authorOrPublished() {
        return resource.data.published == true || request.auth.uid == resource.data.author;
      }

      // Deny any query not limited to 10 or fewer documents
      // Anyone can query published stories
      // Authors can query their unpublished stories
      allow list: if request.query.limit <= 10 &&
                     authorOrPublished();

      // Anyone can retrieve a published story
      // Only a story's author can retrieve an unpublished story
      allow get: if authorOrPublished();

      // Only a story's author can write to a story
      allow write: if request.auth.uid == resource.data.author;
    }

  }
}

แบบสอบถามกลุ่มคอลเลกชันและกฎความปลอดภัย

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

รักษาความปลอดภัยและสืบค้นเอกสารตามกลุ่มคอลเลกชัน

ในกฎความปลอดภัยของคุณ คุณต้องอนุญาตการสืบค้นกลุ่มคอลเลกชันอย่างชัดเจนโดยการเขียนกฎสำหรับกลุ่มคอลเลกชัน:

  1. ตรวจสอบให้แน่ใจว่า rules_version = '2'; เป็นบรรทัดแรกของชุดกฎของคุณ การสืบค้นกลุ่มคอลเล็กชันต้องการลักษณะการทำงาน แบบเรียกซ้ำ {name=**} ของกฎความปลอดภัยเวอร์ชัน 2
  2. เขียนกฎสำหรับกลุ่มคอลเลกชันของคุณโดยใช้ match /{path=**}/ [COLLECTION_ID] /{doc}

ตัวอย่างเช่น พิจารณาฟอรัมที่จัดเป็นเอกสาร forum ที่มีคอลเลกชันย่อย posts :

/forums/{forumid}/posts/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
}

ในแอปพลิเคชันนี้ เราทำให้โพสต์สามารถแก้ไขได้โดยเจ้าของและผู้ใช้ที่ตรวจสอบสิทธิ์สามารถอ่านได้:

service cloud.firestore {
  match /databases/{database}/documents {
    match /forums/{forumid}/posts/{post} {
      // Only authenticated users can read
      allow read: if request.auth != null;
      // Only the post author can write
      allow write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

ผู้ใช้ที่ตรวจสอบสิทธิ์แล้วสามารถดึงโพสต์ของฟอรัมเดียว:

db.collection("forums/technology/posts").get()

แต่ถ้าคุณต้องการแสดงให้ผู้ใช้ปัจจุบันเห็นโพสต์ของพวกเขาในทุกฟอรัม คุณสามารถใช้ แบบสอบถามกลุ่มคอลเลกชัน เพื่อดึงผลลัพธ์จากคอลเลกชัน posts ทั้งหมด:

var user = firebase.auth().currentUser;

db.collectionGroup("posts").where("author", "==", user.uid).get()

ในกฎความปลอดภัยของคุณ คุณต้องอนุญาตการสืบค้นนี้โดยการเขียนกฎการอ่านหรือรายการสำหรับกลุ่มการรวบรวม posts :

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {
    // Authenticated users can query the posts collection group
    // Applies to collection queries, collection group queries, and
    // single document retrievals
    match /{path=**}/posts/{post} {
      allow read: if request.auth != null;
    }
    match /forums/{forumid}/posts/{postid} {
      // Only a post's author can write to a post
      allow write: if request.auth != null && request.auth.uid == resource.data.author;

    }
  }
}

อย่างไรก็ตาม โปรดทราบว่ากฎเหล่านี้จะนำไปใช้กับคอลเลกชันทั้งหมดที่มีการ posts ID โดยไม่คำนึงถึงลำดับชั้น ตัวอย่างเช่น กฎเหล่านี้ใช้กับคอลเล็กชัน posts ทั้งหมดต่อไปนี้:

  • /posts/{postid}
  • /forums/{forumid}/posts/{postid}
  • /forums/{forumid}/subforum/{subforumid}/posts/{postid}

แบบสอบถามกลุ่มคอลเลกชันที่ปลอดภัยตามฟิลด์

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

/forums/{forumid}/posts/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
  published: false
}

จากนั้น เราสามารถเขียนกฎสำหรับกลุ่มรวบรวม posts ตามสถานะที่ published และ author โพสต์ :

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {

    // Returns `true` if the requested post is 'published'
    // or the user authored the post
    function authorOrPublished() {
      return resource.data.published == true || request.auth.uid == resource.data.author;
    }

    match /{path=**}/posts/{post} {

      // Anyone can query published posts
      // Authors can query their unpublished posts
      allow list: if authorOrPublished();

      // Anyone can retrieve a published post
      // Authors can retrieve an unpublished post
      allow get: if authorOrPublished();
    }

    match /forums/{forumid}/posts/{postid} {
      // Only a post's author can write to a post
      allow write: if request.auth.uid == resource.data.author;
    }
  }
}

ด้วยกฎเหล่านี้ ไคลเอนต์เว็บ, Apple และ Android สามารถทำการสอบถามต่อไปนี้:

  • ทุกคนสามารถดึงโพสต์ที่เผยแพร่ในฟอรัม:

    db.collection("forums/technology/posts").where('published', '==', true).get()
    
  • ทุกคนสามารถเรียกดูโพสต์ที่เผยแพร่ของผู้เขียนได้จากทุกฟอรัม:

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • ผู้เขียนสามารถดึงข้อมูลโพสต์ที่ตีพิมพ์และไม่ได้เผยแพร่ทั้งหมดได้จากทุกฟอรัม:

    var user = firebase.auth().currentUser;
    
    db.collectionGroup("posts").where("author", "==", user.uid).get()
    

รักษาความปลอดภัยและสืบค้นเอกสารตามกลุ่มคอลเลกชันและเส้นทางเอกสาร

ในบางกรณี คุณอาจต้องการจำกัดคิวรีกลุ่มคอลเลกชันตามเส้นทางเอกสาร ในการสร้างข้อจำกัดเหล่านี้ คุณสามารถใช้เทคนิคเดียวกันสำหรับการรักษาความปลอดภัยและการสืบค้นเอกสารตามเขตข้อมูล

พิจารณาแอปพลิเคชันที่ติดตามธุรกรรมของผู้ใช้แต่ละรายระหว่างการแลกเปลี่ยนหุ้นและสกุลเงินดิจิทัล:

/users/{userid}/exchange/{exchangeid}/transactions/{transaction}

{
  amount: 100,
  exchange: 'some_exchange_name',
  timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
  user: "some_auth_id",
}

สังเกตฟิลด์ user แม้ว่าเราจะทราบว่าผู้ใช้รายใดเป็นเจ้าของเอกสาร transaction จากเส้นทางของเอกสาร แต่เราทำซ้ำข้อมูลนี้ในเอกสาร transaction แต่ละฉบับเพราะจะช่วยให้เราทำสองสิ่ง:

  • เขียนแบบสอบถามกลุ่มคอลเลกชันที่จำกัดเฉพาะเอกสารที่มี /users/{userid} เฉพาะในเส้นทางเอกสารของพวกเขา ตัวอย่างเช่น:

    var user = firebase.auth().currentUser;
    // Return current user's last five transactions across all exchanges
    db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
    
  • บังคับใช้ข้อจำกัดนี้สำหรับแบบสอบถามทั้งหมดในกลุ่มคอลเลกชัน transactions ดังนั้นผู้ใช้รายหนึ่งจึงไม่สามารถเรียกเอกสาร transaction ของผู้ใช้รายอื่นได้

เราบังคับใช้ข้อจำกัดนี้ในกฎความปลอดภัยของเราและรวมการตรวจสอบข้อมูลสำหรับฟิลด์ user :

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {

    match /{path=**}/transactions/{transaction} {
      // Authenticated users can retrieve only their own transactions
      allow read: if resource.data.user == request.auth.uid;
    }

    match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
      // Authenticated users can write to their own transactions subcollections
      // Writes must populate the user field with the correct auth id
      allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
    }
  }
}

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