3

Is it costly (i.e. roughly how many reads & how much are we charged in $) to have a firestore security like this:

 match /profiles/{document=**} {
   allow create: if request.auth.uid != null
   && (request.resource.data.firstName is string && resource.data.firstName != request.resource.data.firstName)
   && (request.resource.data.lastName is string && resource.data.firstName != request.resource.data.firstName)
  && (request.resource.data.username is string && resource.data.username != request.resource.data.username)
  && (request.resource.data.email is string && resource.data.email != request.resource.data.email)
}
Jek
  • 5,546
  • 9
  • 37
  • 67
  • 1
    You have two distinct questions here that don't overlap at all. Would you please split them up into two questions so that each has a better chance of being answered and accepted independently? Personally, I don't understand your second question. Maybe you should explain what you expect to happen for some example database operations. – Doug Stevenson Dec 20 '18 at 05:02
  • @DougStevenson I have separated them up into 2 questions. Here is the question https://stackoverflow.com/q/53862818/3073280 – Jek Dec 20 '18 at 05:22

2 Answers2

5

According to the documentation, you are billed for get() and exists() requests in your security rules:

Using the get() and exists() functions, your security rules can evaluate incoming requests against other documents in the database.

Continued:

Using these functions executes a read operation in your database, which means you will be billed for reading documents even if your rules reject the request. See Cloud Firestore Pricing for more specific billing information.

So, if your rules don't use get() or exists(), then you don't have additional billing associated with the rules. It appears you are not using either of those functions here, so I would anticipate no additional billing.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • 1
    are those info still valid in 2023 ?, some users are telling me that you get billed for other security rules. – Richardson Jan 12 '23 at 00:16
  • @Richardson No, they are not valid. Accessing "resource" counts towards the billing, I just tested it out with 100 denied requests, and they all counted. And yes, anyone can attack your billing like that. – Beyondo Jul 30 '23 at 02:48
  • @Beyondo What you're saying is not really accurate. I commented on your answer. – Doug Stevenson Jul 30 '23 at 04:08
  • @DougStevenson No, what you are claiming is what is not accurate. Check my comment to your comment on my answer. I modified my answer with references to Firebase's official docs. – Beyondo Jul 30 '23 at 05:47
1

I just tested out this simple security rule:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    function isAuthor() {
      return request.auth != null &&
             request.auth.uid == resource.data.author.uuid;
    }
    match /posts/{postId} {
      allow read, write: if isAuthor();
    }
  }
}

With a simulated read operation and an "incorrect" UID, I read a document 100 times and it was denied 100, and it counted towards the billing 100 times as read operations as you can see in this spike.

A huge spike of 100 sudden read operations despite the security rule denying the request

So Yes, using resource is costly. No matter if it is denied or passed, it'd count towards your billing and you are subject to billing fraud due to Firebase's naturally amazing system.

You'd need to implement a lot of cloud workarounds to mitigate this in production with many users since there's no straight solution or one way to protect against such attacks.

Edit:

Official Firebase Docs says

The resource is the current value within the service represented as a map of key-value pairs. Referencing resource within a condition will result in at most one read of the value from the service. This lookup will count against any service-related quota for the resource. For get requests, the resource will only count toward quota on deny.

That means that if your condition does not reference resource, it will not get counted; it is also more accurate to say that if your condition does not access the resource field, it will not be counted. Bear in mind that even if you accessed resource and your rule succeeded, you'd get only 1 read operation at most as mentioned, not two. It only counts on deny as we've already tested.

(1) This will deny and not count towards your reads:

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false && resource.data
    }
  }
}

(2) This will deny but count towards your reads because you accessed resource.

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if resource.data.readable; // always false
    }
  }
}

(1) Resulted in 0 usage with 100 denied requests.
(2) Resulted in 100 usage with 100 denied requests.

Here's a tip to know if you're going to get billed a read operation or not.

In the Rules Playground:

If you see a resource on the right, that means this rule has accessed resource, which means it billed you a read operation.

Such as: First security rule that accesses resource with resource visible on the right And (.readable is false in the document): Second security rule that accesses resource with resource visible on the right

If you don't see resource on the right, that means this rule has not accessed resource, and thus hasn't billed you a read operation.

Such as (resource.data is always true): Third security rule that does not access resource with resource not visible on the right

References:

  1. https://firebase.google.com/docs/rules/rules-language#basic_structure
Beyondo
  • 2,952
  • 1
  • 17
  • 42
  • @DougStevenson Read behavior will not be the same. It only counts as a read when you access the `resource` field or the rule passed. Check the official docs in "The resource variable" section: https://firebase.google.com/docs/rules/rules-language#basic_structure I updated my answer with both official references and tests. Your second statement is also incorrect. Just because rules are meant for privacy, does not mean they would not stop reads when not accessing `resource` on denied requests cause they, in fact, do. Please do not be so quick on downvoting next time and do some research first. – Beyondo Jul 30 '23 at 05:53
  • I was quick to downvote it *because* you did cite any reference, which is a new thing since the question was asked in 2018. Thanks for clarifying. – Doug Stevenson Jul 30 '23 at 12:48
  • @DougStevenson No problem! I'm glad we're clear and I'll try to always reference new API changes in the future. – Beyondo Jul 30 '23 at 17:13