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

इस पेज पर, सुरक्षा नियमों को स्ट्रक्चर करना और सुरक्षा नियमों के लिए शर्तें लिखना लेखों में बताए गए कॉन्सेप्ट के बारे में ज़्यादा जानकारी दी गई है. इसमें बताया गया है कि 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
}

इसके बाद, हम 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
    }
  }
}

अगले चरण