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

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

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

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

คำค้นหาและกฎความปลอดภัย

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

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

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

/stories/{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สำหรับผลการค้นหาใดๆ ดังนั้น ข้อความค้นหานี้ เป็นไปตามกฎความปลอดภัยและได้รับอนุญาตให้อ่านข้อมูล

คำค้นหา OR รายการ

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

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

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

// These queries will fail
query(db.collection("mydocuments"),
      or(where("x", "==", 1),
         where("x", "==", 6)
      )
    )

query(db.collection("mydocuments"),
      where("x", "in", [1, 3, 6, 42, 99])
    )

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

query(db.collection("mydocuments"),
      or(where("x", "==", 6),
         where("x", "==", 42)
      )
    )

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

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

กฎความปลอดภัยยังยอมรับหรือปฏิเสธการค้นหาตามข้อจำกัดได้ ตัวแปร 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;
    }

  }
}

การค้นหาและกฎความปลอดภัยของกลุ่มคอลเล็กชัน

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

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

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

  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 โดยไม่คำนึงถึงลำดับชั้น ตัวอย่างเช่น กฎเหล่านี้จะใช้กับทุกข้อต่อไปนี้ 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 เนื่องจากช่วยให้เราทำได้ 2 อย่าง ดังนี้

  • เขียนการค้นหากลุ่มคอลเล็กชันที่จำกัดไว้สำหรับเอกสารที่มี /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
    }
  }
}

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