match /form/{id} {
allow write: if hasNotFilledForm() && hasFilledFormAfter();
}
function hasNotFilledForm(){
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.has_filled_out_form == false;
}
function hasFilledFormAfter(){
return getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data.has_filled_out_form == true;
}
Conclusion
So I believe it boils down to knowing how to secure your data. I can easily create a server and a database and allow each request without writing any code for authentication, authorization, validation, etc. So the data can be both insecure and secure by using Firestore just the same as anything else.
Thank you for your reply! You seem to be correct that those rules would work for the examples mentioned in this article.
However, I'd like to mention a couple of points to consider.
As your application starts to grow, if you implement all the necessary security rules (validation, authentication, authorization, logical checks, "batch checking", etc.), your firestore.rules will turn into a huge and messy and unmaintainable god file, making this solution not ideal for larger applications. Also, note that there are many limits on security rules. While those limits are pretty generous, it's worth considering when building larger applications, especially when considering the next couple of points. (Admittedly, this point is not directly related to security but it is still worth considering)
Additionally, as mentioned in the article, firestore security rules do not permit you to import libraries. This means you must either copy and paste source code for libraries you want to use or build a solution yourself. The "copy and paste" method exacerbates the "god file" and limits mentioned above. You must also keep up with updates to the library. The other option, not using a library, can be very dangerous. Most developers won't be able to implement let alone maintain their own implementation of security-related libraries (hence the need for the library in the first place).
I can easily create a server and a database and allow each request without writing any code for authentication, authorization, validation, etc. So the data can be both insecure and secure by using Firestore just the same as anything else.
Yes, you are correct. Anything can be implemented securely and insecurely. I'd like to explain why it's much easier to fail to implement firebase securely than a proper backend.
Most security issues relate to a set of wrong assumptions. Not checking for authentication is an assumption that the user is who they said they are. Not checking for authorization is an assumption that the user is allowed to do what they are trying to do. Poor validation is an assumption that the data the user submits is...well..valid.
All three of these specific assumptions aren't guaranteed in both firestore security rules and a classic server environment. This is we must implement checks to confirm that the user is in fact authenticated and authorized and the input data is valid.
However, there is a very important differentiator. Code written on the backend is guaranteed to run to completion without modification. This cannot be assumed to be true on the frontend, creating yet another layer of checks that needs to be implemented in security rules.
With the batch example from before, if implemented on a proper backend one can assume that both queries WILL run in the order you specified so there is no need to check that the second query will run.
The additional security rules you posted are needed because that same assumption cannot be made about code on the frontend. As you have proven with the fairly simple example of batch queries, it is possible add security rules to check this case but doing so adds another layer where developers who often are not great at security anyway, may make wrong assumptions and implement rules that don't fully check every possible situation.
Well the flaws you mention are easily fixed with rules.
Only increment with 5 points
The or operator is for when other properties than points are updated.
Prevent user from submitting the form multiple times
use batch in frontend:
Conclusion
So I believe it boils down to knowing how to secure your data. I can easily create a server and a database and allow each request without writing any code for authentication, authorization, validation, etc. So the data can be both insecure and secure by using Firestore just the same as anything else.
Thank you for your reply! You seem to be correct that those rules would work for the examples mentioned in this article.
However, I'd like to mention a couple of points to consider.
As your application starts to grow, if you implement all the necessary security rules (validation, authentication, authorization, logical checks, "batch checking", etc.), your
firestore.rules
will turn into a huge and messy and unmaintainable god file, making this solution not ideal for larger applications. Also, note that there are many limits on security rules. While those limits are pretty generous, it's worth considering when building larger applications, especially when considering the next couple of points. (Admittedly, this point is not directly related to security but it is still worth considering)Additionally, as mentioned in the article, firestore security rules do not permit you to import libraries. This means you must either copy and paste source code for libraries you want to use or build a solution yourself. The "copy and paste" method exacerbates the "god file" and limits mentioned above. You must also keep up with updates to the library. The other option, not using a library, can be very dangerous. Most developers won't be able to implement let alone maintain their own implementation of security-related libraries (hence the need for the library in the first place).
Yes, you are correct. Anything can be implemented securely and insecurely. I'd like to explain why it's much easier to fail to implement firebase securely than a proper backend.
Most security issues relate to a set of wrong assumptions. Not checking for authentication is an assumption that the user is who they said they are. Not checking for authorization is an assumption that the user is allowed to do what they are trying to do. Poor validation is an assumption that the data the user submits is...well..valid.
All three of these specific assumptions aren't guaranteed in both firestore security rules and a classic server environment. This is we must implement checks to confirm that the user is in fact authenticated and authorized and the input data is valid.
However, there is a very important differentiator. Code written on the backend is guaranteed to run to completion without modification. This cannot be assumed to be true on the frontend, creating yet another layer of checks that needs to be implemented in security rules.
With the batch example from before, if implemented on a proper backend one can assume that both queries WILL run in the order you specified so there is no need to check that the second query will run.
The additional security rules you posted are needed because that same assumption cannot be made about code on the frontend. As you have proven with the fairly simple example of batch queries, it is possible add security rules to check this case but doing so adds another layer where developers who often are not great at security anyway, may make wrong assumptions and implement rules that don't fully check every possible situation.
This was a good reply with some good points I missed in the article 😊👏
You've nailed it on the head here.
The article fails to represent the power of security rules for validating user input and requests.
Your comparison to setting up a basic database logic layer is brilliant.
See my reply.