Verileri güvenli bir şekilde sorgulayın

Bu sayfa, Cloud Firestore Güvenlik Kurallarının sorgularla nasıl etkileşime girdiğini açıklamak için Güvenlik Kurallarını Yapılandırma ve Güvenlik Kuralları için Koşulları Yazma bölümündeki kavramları temel alır. Güvenlik kurallarının yazabileceğiniz sorguları nasıl etkilediğine daha yakından bakar ve sorgularınızın güvenlik kurallarınızla aynı kısıtlamaları kullanmasını nasıl sağlayacağınızı açıklar. Bu sayfada ayrıca limit ve orderBy gibi sorgu özelliklerine dayalı olarak sorgulara izin vermek veya bunları reddetmek için güvenlik kurallarının nasıl yazılacağı açıklanmaktadır.

Kurallar filtre değildir

Belgeleri almak için sorgu yazarken, güvenlik kurallarının filtre olmadığını, sorguların ya hep ya hiç olduğunu unutmayın. Cloud Firestore, zamandan ve kaynaklardan tasarruf etmenizi sağlamak için bir sorguyu tüm belgelerinizin gerçek alan değerleri yerine potansiyel sonuç kümesine göre değerlendirir. Bir sorgu, istemcinin okuma iznine sahip olmadığı belgeleri potansiyel olarak döndürebilirse isteğin tamamı başarısız olur.

Sorgular ve güvenlik kuralları

Aşağıdaki örneklerin gösterdiği gibi, sorgularınızı güvenlik kurallarınızın kısıtlamalarına uyacak şekilde yazmalısınız.

auth.uid dayalı olarak belgeleri güvence altına alın ve sorgulayın

Aşağıdaki örnek, bir güvenlik kuralıyla korunan belgeleri almak için nasıl sorgu yazılacağını gösterir. story belgelerinin bir koleksiyonunu içeren bir veritabanı düşünün:

/hikayeler/{hikaye kimliği}

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

title ve content alanlarına ek olarak her belge, erişim kontrolü için kullanılacak author ve published alanları saklar. Bu örneklerde uygulamanın, author alanını belgeyi oluşturan kullanıcının UID'sine ayarlamak için Firebase Kimlik Doğrulaması'nı kullandığı varsayılmaktadır. Firebase Authentication ayrıca request.auth değişkenini güvenlik kurallarında da doldurur.

Aşağıdaki güvenlik kuralı, her story okuma ve yazma erişimini yazarına kısıtlamak için request.auth ve resource.data değişkenlerini kullanır:

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

Uygulamanızın, kullanıcıya kendi yazdığı story belgelerinin listesini gösteren bir sayfa içerdiğini varsayalım. Bu sayfayı doldurmak için aşağıdaki sorguyu kullanabileceğinizi bekleyebilirsiniz. Ancak bu sorgu, güvenlik kurallarınızla aynı kısıtlamaları içermediğinden başarısız olacaktır:

Geçersiz : Sorgu kısıtlamaları güvenlik kuralları kısıtlamalarıyla eşleşmiyor

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

Geçerli kullanıcı aslında her story belgesinin yazarı olsa bile sorgu başarısız olur. Bu davranışın nedeni, Cloud Firestore'un güvenlik kurallarınızı uyguladığında sorguyu veritabanınızdaki belgelerin gerçek özelliklerine göre değil, potansiyel sonuç kümesine göre değerlendirmesidir. Bir sorgunun güvenlik kurallarınızı ihlal eden belgeleri içerme olasılığı varsa sorgu başarısız olur.

Buna karşılık, aşağıdaki sorgu başarılı olur çünkü author alanında güvenlik kurallarıyla aynı kısıtlamayı içerir:

Geçerli : Sorgu kısıtlamaları güvenlik kuralları kısıtlamalarıyla eşleşiyor

var user = firebase.auth().currentUser;

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

Belgeleri bir alana göre güvence altına alın ve sorgulayın

Sorgular ve kurallar arasındaki etkileşimi daha iyi göstermek için aşağıdaki güvenlik kuralları, published alanın true olarak ayarlandığı durumlarda herhangi bir kullanıcının story belgelerini okumasına izin verecek şekilde stories koleksiyonuna yönelik okuma erişimini genişletir.

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

Yayınlanan sayfalara ilişkin sorgu, güvenlik kurallarıyla aynı kısıtlamaları içermelidir:

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

.where("published", "==", true) sorgu kısıtlaması, resource.data.published herhangi bir sonuç için true olduğunu garanti eder. Dolayısıyla bu sorgu güvenlik kurallarını karşılıyor ve veri okumasına izin veriliyor.

OR sorguları

Mantıksal bir OR sorgusunu ( or , in veya array-contains-any ) bir kural kümesine göre değerlendirirken Cloud Firestore, her karşılaştırma değerini ayrı ayrı değerlendirir. Her karşılaştırma değeri güvenlik kuralı kısıtlamalarını karşılamalıdır. Örneğin aşağıdaki kural için:

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

Geçersiz : Sorgu, tüm potansiyel belgeler için x > 5 olacağını garanti etmez

// 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])
    )

Geçerli : Sorgu, tüm potansiyel belgeler için x > 5 olmasını garanti eder

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

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

Sorgulardaki kısıtlamaları değerlendirme

Güvenlik kurallarınız, kısıtlamalarına göre sorguları da kabul edebilir veya reddedebilir. request.query değişkeni bir sorgunun limit , offset ve orderBy özelliklerini içerir. Örneğin, güvenlik kurallarınız, alınan maksimum belge sayısını belirli bir aralıkla sınırlamayan tüm sorguları reddedebilir:

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

Aşağıdaki kural kümesi, sorgulara uygulanan kısıtlamaları değerlendiren güvenlik kurallarının nasıl yazılacağını gösterir. Bu örnek, önceki stories kural kümesini aşağıdaki değişikliklerle genişletir:

  • Kural seti okuma kuralını get ve list kurallarına ayırır.
  • get kuralı, tekli belgelerin alınmasını genel belgelerle veya kullanıcının yazdığı belgelerle sınırlar.
  • list kuralı sorgular için get kuralıyla aynı kısıtlamaları uygular. Ayrıca sorgu limitini de kontrol eder, ardından limitsiz veya 10'dan büyük limitli sorguları reddeder.
  • Kural seti, kod tekrarını önlemek için bir authorOrPublished() fonksiyonunu tanımlar.
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;
    }

  }
}

Koleksiyon grubu sorguları ve güvenlik kuralları

Varsayılan olarak sorgular tek bir koleksiyonun kapsamına alınır ve sonuçları yalnızca bu koleksiyondan alır. Koleksiyon grubu sorguları ile aynı kimliğe sahip tüm koleksiyonlardan oluşan bir koleksiyon grubundan sonuçları alabilirsiniz. Bu bölümde, güvenlik kurallarını kullanarak koleksiyon grubu sorgularınızın güvenliğini nasıl sağlayacağınız açıklanmaktadır.

Koleksiyon gruplarına göre belgeleri güvence altına alın ve sorgulayın

Güvenlik kurallarınızda, koleksiyon grubu için bir kural yazarak koleksiyon grubu sorgularına açıkça izin vermelisiniz:

  1. rules_version = '2'; kural kümenizin ilk satırıdır. Koleksiyon grubu sorguları, güvenlik kuralları sürüm 2'nin yeni özyinelemeli joker karakter {name=**} davranışını gerektirir.
  2. match /{path=**}/ [COLLECTION_ID] /{doc} kullanarak koleksiyon grubunuz için bir kural yazın.

Örneğin, posts alt koleksiyonlarını içeren forum belgeleri halinde düzenlenmiş bir forum düşünün:

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

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

Bu uygulamada gönderileri sahipleri tarafından düzenlenebilir ve kimliği doğrulanmış kullanıcılar tarafından okunabilir hale getiriyoruz:

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

Kimliği doğrulanmış herhangi bir kullanıcı herhangi bir forumun gönderilerini alabilir:

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

Peki ya mevcut kullanıcıya tüm forumlardaki gönderilerini göstermek istiyorsanız? Tüm posts koleksiyonlarından sonuçları almak için koleksiyon grubu sorgusunu kullanabilirsiniz:

var user = firebase.auth().currentUser;

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

Güvenlik kurallarınızda, posts koleksiyonu grubu için bir okuma veya listeleme kuralı yazarak bu sorguya izin vermelisiniz:

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;

    }
  }
}

Ancak bu kuralların, hiyerarşiden bağımsız olarak kimlik posts içeren tüm koleksiyonlar için geçerli olacağını unutmayın. Örneğin, bu kurallar aşağıdaki posts koleksiyonlarının tümü için geçerlidir:

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

Bir alanı temel alan güvenli koleksiyon grubu sorguları

Tek koleksiyon sorguları gibi, koleksiyon grubu sorgularının da güvenlik kurallarınız tarafından belirlenen kısıtlamalara uyması gerekir. Örneğin, yukarıdaki stories örneğinde yaptığımız gibi her forum gönderisine published bir alan ekleyebiliriz:

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

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

Daha sonra published durumuna ve gönderinin author göre posts koleksiyonu grubu için kurallar yazabiliriz:

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

Bu kurallarla Web, Apple ve Android istemcileri aşağıdaki sorguları yapabilir:

  • Herkes bir forumda yayınlanmış gönderileri alabilir:

    db.collection("forums/technology/posts").where('published', '==', true).get()
    
  • Herkes bir yazarın tüm forumlarda yayınlanmış gönderilerine erişebilir:

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • Yazarlar tüm forumlardaki yayınlanmış ve yayınlanmamış tüm gönderilerine erişebilir:

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

Koleksiyon grubuna ve belge yoluna göre belgeleri güvence altına alın ve sorgulayın

Bazı durumlarda koleksiyon grubu sorgularını belge yoluna göre kısıtlamak isteyebilirsiniz. Bu kısıtlamaları oluşturmak için, belgeleri bir alana göre güvence altına almak ve sorgulamak için aynı teknikleri kullanabilirsiniz.

Her kullanıcının çeşitli hisse senedi ve kripto para borsaları arasındaki işlemlerini takip eden bir uygulama düşünün:

/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 alanına dikkat edin. Belgenin yolundan hangi kullanıcının bir transaction belgesine sahip olduğunu bilsek bile, bu bilgiyi her transaction belgesinde kopyalarız çünkü bu bize iki şey yapmamıza olanak tanır:

  • Belge yolunda belirli bir /users/{userid} içeren belgelerle sınırlı koleksiyon grubu sorguları yazın. Örneğin:

    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)
    
  • Bir kullanıcının başka bir kullanıcının transaction belgelerini alamamasını sağlamak için, transactions koleksiyonu grubundaki tüm sorgular için bu kısıtlamayı uygulayın.

Bu kısıtlamayı güvenlik kurallarımızda uyguluyoruz ve user alanı için veri doğrulamayı dahil ediyoruz:

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

Sonraki adımlar