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 Authentication เพื่อตั้งค่าฟิลด์ 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

เมื่อประเมินส่วนคำสั่งการค้นหา 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 :

/ฟอรัม/{ฟอรั่ม}/โพสต์/{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 ด้านบน:

/ฟอรัม/{ฟอรั่ม}/โพสต์/{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
    }
  }
}

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