Verileri güvenli bir şekilde sorgulayın

Bu sayfada, Cloud Firestore Güvenlik Kurallarının sorgularla nasıl etkileşime girdiğini açıklamak amacıyla Güvenlik Kurallarını Yapılandırma ve Güvenlik Kuralları İçin Koşullar Yazma bölümlerindeki kavramlar temel alınmıştır. Bu yazıda, güvenlik kurallarının yazabileceğiniz sorguları nasıl etkilediği ele alınmış ve sorgularınızın güvenlik kurallarınızla aynı kısıtlamaları kullanmasını nasıl sağlayacağınız açıklanmıştır. Bu sayfada, limit ve orderBy gibi sorgu özelliklerine dayalı sorgulara izin vermek veya sorguları reddetmek için güvenlik kurallarının nasıl yazılacağı da açıklanmaktadır.

Kurallar filtre değildir

Belgeleri almak için sorgu yazarken güvenlik kurallarının filtre olmadığını unutmayın. Sorguların hepsi ya da hiç değildir. Cloud Firestore, zamandan ve kaynaklardan tasarruf etmenizi sağlamak için sorguyu tüm belgeleriniz için gerçek alan değerleri yerine potansiyel sonuç kümesine göre değerlendirir. Bir sorgu, istemcinin okuma izninin olmadığı dokümanları potansiyel olarak döndürebiliyorsa isteğin tamamı başarısız olur.

Sorgular ve güvenlik kuralları

Aşağıdaki örneklerde gösterildiği gibi sorgularınızı güvenlik kurallarınızın kısıtlamalarına uyacak şekilde yazmanız gerekir.

auth.uid temelinde belgelerin güvenliğini sağlayın ve sorgulayın

Aşağıdaki örnekte, bir güvenlik kuralıyla korunan dokümanları almak için sorgunun nasıl yazılacağı gösterilmektedir. story belge içeren bir veritabanı düşünün:

/Hikayeler/{storyid}

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

Her belgede, title ve content alanlarına ek olarak author ve published alanları da erişim denetimi için depolanır. Bu örneklerde, uygulamanın author alanını dokümanı oluşturan kullanıcının UID'sine ayarlamak için Firebase Authentication kullandığı varsayılmıştır. Firebase Authentication, güvenlik kurallarındaki request.auth değişkenini de doldurur.

Aşağıdaki güvenlik kuralı, her bir story öğesinin okuma ve yazma erişimini yazarıyla 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ızda, kullanıcıya yazdığı story dokümanlarının listesini gösteren bir sayfa içerdiğini varsayalım. Bu sayfayı doldurmak için aşağıdaki sorguyu kullanabilirsiniz. Ancak bu sorgu, güvenlik kurallarınızla aynı kısıtlamaları içermediği için başarısız olur:

Geçersiz: Sorgu kısıtlamaları, güvenlik kuralı kısıtlamalarıyla eşleşmez

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

Geçerli kullanıcı gerçekten her story dokümanının 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, olası sonuç kümesine göre değerlendirmesidir. Bir sorgu, güvenlik kurallarınızı ihlal eden belgeleri potansiyel olarak içerebilirse sorgu başarısız olur.

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

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

var user = firebase.auth().currentUser;

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

Bir alana göre belgelerin güvenliğini sağlayın ve sorgulayın

Sorgular ve kurallar arasındaki etkileşimi daha iyi göstermek için aşağıdaki güvenlik kuralları, stories koleksiyonunun okuma erişimini genişleterek tüm kullanıcıların, published alanının true olarak ayarlandığı story dokümanlarını okumasına izin verir.

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 sayfalar için yapılan 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ı, tüm sonuçlar için resource.data.published değerinin true olduğunu garanti eder. Bu nedenle, bu sorgu güvenlik kurallarını karşılar ve verileri okuma iznine sahip olur.

OR sorgu

Cloud Firestore, mantıksal bir OR sorgusunu (or, in veya array-contains-any) kural grubuna göre değerlendirirken her karşılaştırma değerini ayrı olarak 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

// 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 dokümanlar için x > 5 işleminin

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

Ayrıca güvenlik kurallarınız, kısıtlamalarına göre sorguları kabul edebilir veya reddedebilir. request.query değişkeni, sorgunun limit, offset ve orderBy özelliklerini içerir. Örneğin, güvenlik kurallarınız, alınan maksimum doküman 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 yerleştirilen kısıtlamaları değerlendiren güvenlik kurallarının nasıl yazılacağını gösterir. Bu örnek, önceki stories kuralını aşağıdaki değişikliklerle genişletir:

  • Kural kümesi, okuma kuralını get ve list için kurallara ayırır.
  • get kuralı, tek dokümanların alınmasını herkese açık dokümanlarla veya kullanıcının yazdığı dokümanlarla kısıtlar.
  • list kuralı, sorgular için get ile aynı kısıtlamaları uygular. Ayrıca sorgu sınırını da kontrol eder ve sınırsız olarak veya 10'dan büyük bir sınıra sahip sorguları reddeder.
  • Kural kümesi, kodun yinelenmesini önlemek için bir authorOrPublished() işlevi 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 koleksiyonu kapsar ve yalnızca bu koleksiyondan sonuçları alır. Koleksiyon grubu sorguları sayesinde, aynı kimliğe sahip tüm koleksiyonları içeren 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 dokümanların güvenliğini sağlayın ve sorgulayın

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

  1. rules_version = '2'; değerinin, kural grubunuzun ilk satırı olduğundan emin olun. Toplama grubu sorguları, güvenlik kuralları sürüm 2'nin yeni yinelenen 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 koleksiyon içeren forum dokümanlar halinde düzenlenmiş bir forumu ele alalım:

/forumlar/{forumid}/posts/{postid}

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

Bu uygulamada, yayınları 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ğrulanan kullanıcılar herhangi bir forumun yayınlarını alabilir:

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

Peki, mevcut kullanıcıya yayınlarını tüm forumlarda göstermek isterseniz ne olur? Tüm posts koleksiyonlarından sonuçları almak için bir koleksiyon grubu sorgusu kullanabilirsiniz:

var user = firebase.auth().currentUser;

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

Güvenlik kurallarınızda, posts koleksiyon grubu için bir okuma veya liste kuralı yazarak bu sorguya izin vermeniz gerekir:

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 posts kimliğine sahip 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}

Alan bazında güvenli toplama grubu sorguları

Tek koleksiyonlu sorgularda olduğu gibi, koleksiyon grubu sorguları da güvenlik kurallarınız tarafından belirlenen kısıtlamaları karşılamalıdır. Örneğin, yukarıdaki stories örneğinde yaptığımız gibi her forum yayınına bir published alanı ekleyebiliriz:

/forumlar/{forumid}/posts/{postid}

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

Ardından, posts koleksiyon grubu için published durumuna ve author yayınına göre 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;
    }
  }
}

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

  • Herkes forumda paylaşılan yayınları alabilir:

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

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • Yazarlar, tüm forumlarda yayınlanan ve yayınlanmayan tüm yayınlarını alabilir:

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

Koleksiyon grubuna ve belge yoluna göre dokümanların güvenliğini sağlayı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 bir alana göre dokümanları güvenli hale getirmek ve sorgulamak üzere aynı teknikleri kullanabilirsiniz.

Her kullanıcının çeşitli borsalar ve kripto para birimi borsalarındaki işlemlerini izleyen bir uygulama düşünün:

/users/{userid}/exchange/{exchangeid}/transactions/{işlem}

{
  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. Dokümanın yolundan transaction dokümanına hangi kullanıcının sahip olduğunu bilsek de, bu bilgileri her transaction dokümanında iki şekilde tekrarlıyoruz:

  • Belge yolunda belirli bir /users/{userid} içeren belgelerle kısıtlanmış koleksiyon grubu sorguları yazın. Örnek:

    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 dokümanlarını alamaması için bu kısıtlamayı transactions koleksiyon grubundaki tüm sorgular için uygulayın.

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

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