डेटा को सुरक्षित तरीके से क्वेरी करें

इस पेज पर, सुरक्षा नियमों को व्यवस्थित करने और सुरक्षा नियमों के लिए शर्तें लिखने के कॉन्सेप्ट के आधार पर बताया गया है कि Cloud Firestore Security Rules क्वेरी के साथ कैसे इंटरैक्ट करता है. इसमें इस बात पर बारीकी से नज़र डाली गई है कि सुरक्षा नियम, लिखी जा सकने वाली क्वेरी पर कैसे असर डालते हैं. साथ ही, यह पक्का करने का तरीका भी बताया गया है कि आपकी क्वेरी में, सुरक्षा नियमों जैसी ही पाबंदियों का इस्तेमाल किया जाए. इस पेज पर, 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 फ़ील्ड भी सेव होते हैं. इन उदाहरणों में यह माना गया है कि ऐप्लिकेशन, दस्तावेज़ बनाने वाले उपयोगकर्ता के यूआईडी पर author फ़ील्ड को सेट करने के लिए, Firebase Authentication का इस्तेमाल करता है. Firebase Authentication, सुरक्षा नियमों में 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'; आपके नियमों के सेट की पहली लाइन हो. कलेक्शन ग्रुप क्वेरी के लिए, सुरक्षा नियमों के वर्शन 2 के नए बार-बार इस्तेमाल होने वाले वाइल्डकार्ड {name=**} के व्यवहार की ज़रूरत होती है.
  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
}

इसके बाद, हम published स्थिति और पोस्ट author के आधार पर, posts कलेक्शन ग्रुप के लिए नियम लिख सकते हैं:

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
    }
  }
}

अगले चरण