Learn how to hack a Firebase app, then configure solid Firestore Database security rules to ensure data integrity. https://angularfirebase.com/lessons/firestore-security-rules-guide/

- Rules Reference: https://firebase.google.com/docs/firestore/reference/security/
2024-02-04T07:42:48.107Z
nova-2
2-general-nova
2024-01-09.29447
30089e05-99d1-4376-b32e-c263170674af
5335823f-3897-4e09-994b-86d4a0202bcb
83726d6b5aed17dd737b29aa39375c9ac95ec253b2964b35189328908a6cdce4
67875a7f-c9c4-48a0-aa55-5bdb8a91c34a
deprecated
I can hack into your Firebase app in about 10 seconds, then steal and delete all your data, assuming you failed to set up back end security rules.
Allow me to show you.
All I have to do is pull up an app and go into the Chrome developer tools under the network tab, then find a request coming from Firebase, open up the headers, and find the corresponding project ID.
From there, I can just go to the landline, and run a curl delete request to the Firebase REST API and start wiping out this app's database.
If you deploy a Firebase app without back end rules, Anybody can do this to your app as well.
Today, I'm going to show you how to keep your Firestore database secure by writing expressive, easy to understand back end security rules.
If you're new here, like and subscribe, and grab the source code from angularfirebase.com.
To get started, you'll wanna go to the Firestore database, and then open up the rules tab.
Rules are defined in their own special language that somewhat resembles JavaScript.
Currently, the arrow's pointing to the root of our database, and all of Our rules logic will be defined inside this block.
Then we use the match keyword to point to paths in our database where we want to apply rules.
In this case, we're using the equals star star to match every single document in the database.
Then we use the allow keyword followed by the operation that we wanna set a rule for, in this case, read or write.
If we leave it blank, it's going to allow those operations, or we can write an expression that returns true or false to apply some actual rule logic to this route.
For example, if we wanted to completely lock down all documents, we could say if false, and then nobody could read or write from the client side.
If I try to query a collection from my front end app, I'm going to get an error in the browser console.
So cool.
Now our app's 100% secure, but that's a little too secure.
Now we're going to write some rules that perform logic based on user authentication, the underlying data, the incoming request time, etcetera.
First, let's look at the various types of requests that we can lock down.
First, we have get, which would apply to reading a specific document.
Then list would apply to a collection query.
So those are our read rules.
Then our write rules, we have create, which applies to creating new data, update for modifying existing data, and delete for removing data.
In addition, we could say allow read, which would just combine get and list, or we could also say, allow write, which would combine create, update, delete into a single rule.
So that's how we scope rules to specific operations.
Now I wanna show you how to point to the actual data that we want to apply these rules to.
The first thing to make note of is the equal star star.
That special syntax is going to tell your rules to cascade down to all subcollections and anything nested under that path.
That's useful when you have a rule that's applied to many different collections, such as verifying a user is authenticated.
You can also make your rules very specific.
For example, we can point to a specific document using the document ID.
All you have to do is hard code it directly into the path and then write the corresponding rules.
That can be useful sometimes, but the single most useful matcher is the wildcard ID.
Instead of hard coding the ID, we just add brackets with a variable name that we can evaluate as the ID at runtime.
By setting product ID right here, we can use that as a variable that represents the document ID.
Then we can evaluate that ID in the actual allow statement, which we'll see in action here throughout the lesson.
And the wildcard is by far the most common matcher that you're going to be using throughout your back end rules.
I'd also like to point out that your rules are secure by default.
So unless you explicitly allow an operation, it's going to be blocked by Firestore.
In my demo here, I'm allowing the user to read the documents, but only delete a document if they are logged in.
We can figure that out by looking at the request object that is built into the rules environment.
The request object is very important, and for one, it gives us information about the current user.
To simply see if a user is logged in, we can see if the request auth does not equal null.
So that's pretty cool, but the thing I don't like about it is that it doesn't read very well.
That brings me to my favorite part of Firestore rules and that's the ability to write your own custom functions.
Functions allow us to repurpose our rules in a way that's dry and readable.
So instead of writing if request auth does not equal null a whole bunch of times, I'm going to write a function called is signed in.
The function will just return the request auth does not equal null statement.
Then we can replace it on the allow line, and now it just reads like a plain English sentence that anybody can understand.
Allow delete if signed in.
Pretty simple.
My goal from here on out is to give you a bunch of helper functions that you can easily reuse in your own project.
The next thing we might wanna do is determine whether or not a user is the owner of a certain document.
A good example might be a user profile where all users can read other users' profiles, but only the owner can write to it.
So here, we're matching the user's user ID document and allowing read if they're signed in, but only allowing write if they're the owner user.
And notice how we're passing the wildcard variable to the argument in our function.
For this to work, it's important that the document ID matches the user ID.
And if you've seen my authentication tutorials, you'll know how to do that.
Then when defining this function, all we have to do is look at the auth user ID on the request and compare it to the user ID on that document that we passed as the argument.
If the IDs are the same, then we know that the current user is the owner of this document.
And we can actually get a lot more sophisticated.
Let's say we want to determine whether or not a user has a verified email.
I'm going to use an and statement to chain another function to our rule.
With an and statement, both functions will need to return true for this rule to pass.
You can also chain things together with an or statement to check if only one of the conditions is true.
But in this case, we wanna make sure the user's the owner and that they have a verified email.
Like I mentioned before, the request object has all kinds of useful information.
In this case, we're going to look at the token on the auth object that contains an email verified property of true or false.
We could also see when a user signed up, if they're anonymous, if they have a phone number, to compose all kinds of other useful rules.
But another common thing you'll need in your rules is to know what the existing data looks like as compared to the incoming data.
To get the existing data, you can use the resource keyword followed by data, but try not to confuse that with request resource data.
I recommend writing functions as existing data and incoming data just to make this very explicit and clear because these are easy to mix up.
And it's really important that you don't mess up your back end rules.
So when might you need existing data?
A common use case would be if you have a certain document that maybe gets locked after it's published.
If we jump up to the products path, we can look at the existing product document, and we'll make sure that it's not locked before running the update.
Checking existing data is usually most important when your users can control whether or not a document should be modified at some point.
When it comes to incoming data, there's all kinds of validation rules that we can apply to our underlying data structure.
Let's imagine that our product document must have a price of greater than $10.
So if the user tries to send an update where the price is less than $10, we're going to cause this update to fail.
So looking at the incoming data is very important for maintaining the integrity of your database.
Now switching gears, in episode 75, I talked about role based user authorization, And I want to revisit some of those concepts now.
In that lesson, we saved information about the user's role on their user document, And we need to read that document when applying a rule to various parts of the database.
This document is not going to be available on the user object, So we're going to need a different mechanism to read the document whenever needed.
The rules environment has a get keyword that will read a document by pointing to a specific path.
We need to use the absolute path, so this gets pretty verbose.
But if you put it into a function, then it's not too bad.
We write out the path like normal and then use dollar sign parenthesis and interpolate the request auth user ID.
This gives us the current user's document in Firestore, which contains information about their authorized roles.
Now let's use this function to authorize a user to update product.
We can read the document, and then we have an object of roles.
So we can get all of the keys from that object by calling keys on it.
Then has any will check if the user has any of these authorized roles such as editor or admin.
You could also use has all to make sure that the user has all of these roles.
At the end of the day, that gives you a very simple solution for implementing role based user authorization.
Another interesting thing to think about is how time impacts your database security.
A common scenario is to throttle the amount of data that a user can create during a certain duration of time.
We can get the current time of any request by just calling request time.
Then there's a duration helper that we can use to operate on the time stamp.
For example, we wanna make sure the request time comes after the created at timestamp plus a duration of 60 seconds.
In other words, the user can only write to a product document every 60 seconds.
So duration just allows you to add a number of seconds, minutes, or hours numbers and compare them to a Firestore timestamp.
I'm going to go ahead and wrap up there.
Hopefully, this helps you avoid becoming the next major data breach victim.
If this video helped you, please like and subscribe.
And if you're ready to take things to the next level, consider becoming a pro subscriber at angularfirebase.com.
You'll get a whole bunch of exclusive content and direct access to chat with me on Slack.
Thanks for watching, and I'll see you soon.

Speaker 0: I can hack into your Firebase app in about 10 seconds, then steal and delete all your data, assuming you failed to set up back end security rules. Allow me to show you. All I have to do is pull up an app and go into the Chrome developer tools under the network tab, then find a request coming from Firebase, open up the headers, and find the corresponding project ID. From there, I can just go to the landline, and run a curl delete request to the Firebase REST API and start wiping out this app's database. If you deploy a Firebase app without back end rules, Anybody can do this to your app as well.

Today, I'm going to show you how to keep your Firestore database secure by writing expressive, easy to understand back end security rules. If you're new here, like and subscribe, and grab the source code from angularfirebase.com. To get started, you'll wanna go to the Firestore database, and then open up the rules tab. Rules are defined in their own special language that somewhat resembles JavaScript. Currently, the arrow's pointing to the root of our database, and all of Our rules logic will be defined inside this block.

Then we use the match keyword to point to paths in our database where we want to apply rules. In this case, we're using the equals star star to match every single document in the database. Then we use the allow keyword followed by the operation that we wanna set a rule for, in this case, read or write. If we leave it blank, it's going to allow those operations, or we can write an expression that returns true or false to apply some actual rule logic to this route. For example, if we wanted to completely lock down all documents, we could say if false, and then nobody could read or write from the client side.

If I try to query a collection from my front end app, I'm going to get an error in the browser console. So cool. Now our app's 100% secure, but that's a little too secure. Now we're going to write some rules that perform logic based on user authentication, the underlying data, the incoming request time, etcetera. First, let's look at the various types of requests that we can lock down.

First, we have get, which would apply to reading a specific document. Then list would apply to a collection query. So those are our read rules. Then our write rules, we have create, which applies to creating new data, update for modifying existing data, and delete for removing data. In addition, we could say allow read, which would just combine get and list, or we could also say, allow write, which would combine create, update, delete into a single rule.

So that's how we scope rules to specific operations. Now I wanna show you how to point to the actual data that we want to apply these rules to. The first thing to make note of is the equal star star. That special syntax is going to tell your rules to cascade down to all subcollections and anything nested under that path. That's useful when you have a rule that's applied to many different collections, such as verifying a user is authenticated.

You can also make your rules very specific. For example, we can point to a specific document using the document ID. All you have to do is hard code it directly into the path and then write the corresponding rules. That can be useful sometimes, but the single most useful matcher is the wildcard ID. Instead of hard coding the ID, we just add brackets with a variable name that we can evaluate as the ID at runtime.

By setting product ID right here, we can use that as a variable that represents the document ID. Then we can evaluate that ID in the actual allow statement, which we'll see in action here throughout the lesson. And the wildcard is by far the most common matcher that you're going to be using throughout your back end rules. I'd also like to point out that your rules are secure by default. So unless you explicitly allow an operation, it's going to be blocked by Firestore.

In my demo here, I'm allowing the user to read the documents, but only delete a document if they are logged in. We can figure that out by looking at the request object that is built into the rules environment. The request object is very important, and for one, it gives us information about the current user. To simply see if a user is logged in, we can see if the request auth does not equal null. So that's pretty cool, but the thing I don't like about it is that it doesn't read very well.

That brings me to my favorite part of Firestore rules and that's the ability to write your own custom functions. Functions allow us to repurpose our rules in a way that's dry and readable. So instead of writing if request auth does not equal null a whole bunch of times, I'm going to write a function called is signed in. The function will just return the request auth does not equal null statement. Then we can replace it on the allow line, and now it just reads like a plain English sentence that anybody can understand.

Allow delete if signed in. Pretty simple. My goal from here on out is to give you a bunch of helper functions that you can easily reuse in your own project. The next thing we might wanna do is determine whether or not a user is the owner of a certain document. A good example might be a user profile where all users can read other users' profiles, but only the owner can write to it.

So here, we're matching the user's user ID document and allowing read if they're signed in, but only allowing write if they're the owner user. And notice how we're passing the wildcard variable to the argument in our function. For this to work, it's important that the document ID matches the user ID. And if you've seen my authentication tutorials, you'll know how to do that. Then when defining this function, all we have to do is look at the auth user ID on the request and compare it to the user ID on that document that we passed as the argument.

If the IDs are the same, then we know that the current user is the owner of this document. And we can actually get a lot more sophisticated. Let's say we want to determine whether or not a user has a verified email. I'm going to use an and statement to chain another function to our rule. With an and statement, both functions will need to return true for this rule to pass.

You can also chain things together with an or statement to check if only one of the conditions is true. But in this case, we wanna make sure the user's the owner and that they have a verified email. Like I mentioned before, the request object has all kinds of useful information. In this case, we're going to look at the token on the auth object that contains an email verified property of true or false. We could also see when a user signed up, if they're anonymous, if they have a phone number, to compose all kinds of other useful rules.

But another common thing you'll need in your rules is to know what the existing data looks like as compared to the incoming data. To get the existing data, you can use the resource keyword followed by data, but try not to confuse that with request resource data. I recommend writing functions as existing data and incoming data just to make this very explicit and clear because these are easy to mix up. And it's really important that you don't mess up your back end rules. So when might you need existing data?

A common use case would be if you have a certain document that maybe gets locked after it's published. If we jump up to the products path, we can look at the existing product document, and we'll make sure that it's not locked before running the update. Checking existing data is usually most important when your users can control whether or not a document should be modified at some point. When it comes to incoming data, there's all kinds of validation rules that we can apply to our underlying data structure. Let's imagine that our product document must have a price of greater than $10.

So if the user tries to send an update where the price is less than $10, we're going to cause this update to fail. So looking at the incoming data is very important for maintaining the integrity of your database. Now switching gears, in episode 75, I talked about role based user authorization, And I want to revisit some of those concepts now. In that lesson, we saved information about the user's role on their user document, And we need to read that document when applying a rule to various parts of the database. This document is not going to be available on the user object, So we're going to need a different mechanism to read the document whenever needed.

The rules environment has a get keyword that will read a document by pointing to a specific path. We need to use the absolute path, so this gets pretty verbose. But if you put it into a function, then it's not too bad. We write out the path like normal and then use dollar sign parenthesis and interpolate the request auth user ID. This gives us the current user's document in Firestore, which contains information about their authorized roles.

Now let's use this function to authorize a user to update product. We can read the document, and then we have an object of roles. So we can get all of the keys from that object by calling keys on it. Then has any will check if the user has any of these authorized roles such as editor or admin. You could also use has all to make sure that the user has all of these roles.

At the end of the day, that gives you a very simple solution for implementing role based user authorization. Another interesting thing to think about is how time impacts your database security. A common scenario is to throttle the amount of data that a user can create during a certain duration of time. We can get the current time of any request by just calling request time. Then there's a duration helper that we can use to operate on the time stamp.

For example, we wanna make sure the request time comes after the created at timestamp plus a duration of 60 seconds. In other words, the user can only write to a product document every 60 seconds. So duration just allows you to add a number of seconds, minutes, or hours numbers and compare them to a Firestore timestamp. I'm going to go ahead and wrap up there. Hopefully, this helps you avoid becoming the next major data breach victim.

If this video helped you, please like and subscribe. And if you're ready to take things to the next level, consider becoming a pro subscriber at angularfirebase.com. You'll get a whole bunch of exclusive content and direct access to chat with me on Slack. Thanks for watching, and I'll see you soon.
I can hack into your Firebase app in about 10 seconds, then steal and delete all your data, assuming you failed to set up back end security rules. Allow me to show you. All I have to do is pull up an app and go into the Chrome developer tools under the network tab, then find a request coming from Firebase, open up the headers, and find the corresponding project ID. From there, I can just go to the landline, and run a curl delete request to the Firebase REST API and start wiping out this app's database. If you deploy a Firebase app without back end rules, Anybody can do this to your app as well. Today, I'm going to show you how to keep your Firestore database secure by writing expressive, easy to understand back end security rules. If you're new here, like and subscribe, and grab the source code from angularfirebase.com. To get started, you'll wanna go to the Firestore database, and then open up the rules tab.
Rules are defined in their own special language that somewhat resembles JavaScript. Currently, the arrow's pointing to the root of our database, and all of Our rules logic will be defined inside this block. Then we use the match keyword to point to paths in our database where we want to apply rules.
logic
In this case, we're using the equals star star to match every single document in the database.
Then we use the allow keyword followed by the operation that we wanna set a rule for, in this case, read or write. If we leave it blank, it's going to allow those operations, or we can write an expression that returns true or false to apply some actual rule logic to this route.
logic
operations
For example, if we wanted to completely lock down all documents, we could say if false, and then nobody could read or write from the client side.
If I try to query a collection from my front end app, I'm going to get an error in the browser console. So cool. Now our app's 100% secure, but that's a little too secure. Now we're going to write some rules that perform logic based on user authentication, the underlying data, the incoming request time, etcetera. First, let's look at the various types of requests that we can lock down. First, we have get, which would apply to reading a specific document. Then list would apply to a collection query. So those are our read rules. Then our write rules, we have create, which applies to creating new data, update for modifying existing data, and delete for removing data. In addition, we could say allow read, which would just combine get and list, or we could also say, allow write, which would combine create, update, delete into a single rule.
security
logic
reading
documentation
So that's how we scope rules to specific operations. Now I wanna show you how to point to the actual data that we want to apply these rules to. The first thing to make note of is the equal star star. That special syntax is going to tell your rules to cascade down to all subcollections and anything nested under that path.
operations
That's useful when you have a rule that's applied to many different collections, such as verifying a user is authenticated. You can also make your rules very specific. For example, we can point to a specific document using the document ID. All you have to do is hard code it directly into the path and then write the corresponding rules.
documentation
That can be useful sometimes, but the single most useful matcher is the wildcard ID. Instead of hard coding the ID, we just add brackets with a variable name that we can evaluate as the ID at runtime. By setting product ID right here, we can use that as a variable that represents the document ID. Then we can evaluate that ID in the actual allow statement, which we'll see in action here throughout the lesson. And the wildcard is by far the most common matcher that you're going to be using throughout your back end rules. I'd also like to point out that your rules are secure by default. So unless you explicitly allow an operation, it's going to be blocked by Firestore. In my demo here, I'm allowing the user to read the documents, but only delete a document if they are logged in. We can figure that out by looking at the request object that is built into the rules environment. The request object is very important, and for one, it gives us information about the current user. To simply see if a user is logged in, we can see if the request auth does not equal null. So that's pretty cool, but the thing I don't like about it is that it doesn't read very well. That brings me to my favorite part of Firestore rules and that's the ability to write your own custom functions. Functions allow us to repurpose our rules in a way that's dry and readable. So instead of writing if request auth does not equal null a whole bunch of times, I'm going to write a function called is signed in. The function will just return the request auth does not equal null statement. Then we can replace it on the allow line, and now it just reads like a plain English sentence that anybody can understand. Allow delete if signed in. Pretty simple. My goal from here on out is to give you a bunch of helper functions that you can easily reuse in your own project. The next thing we might wanna do is determine whether or not a user is the owner of a certain document. A good example might be a user profile where all users can read other users' profiles, but only the owner can write to it. So here, we're matching the user's user ID document and allowing read if they're signed in, but only allowing write if they're the owner user. And notice how we're passing the wildcard variable to the argument in our function. For this to work, it's important that the document ID matches the user ID. And if you've seen my authentication tutorials, you'll know how to do that. Then when defining this function, all we have to do is look at the auth user ID on the request and compare it to the user ID on that document that we passed as the argument.
If the IDs are the same, then we know that the current user is the owner of this document. And we can actually get a lot more sophisticated. Let's say we want to determine whether or not a user has a verified email. I'm going to use an and statement to chain another function to our rule.
email
With an and statement, both functions will need to return true for this rule to pass. You can also chain things together with an or statement to check if only one of the conditions is true.
But in this case, we wanna make sure the user's the owner and that they have a verified email. Like I mentioned before, the request object has all kinds of useful information. In this case, we're going to look at the token on the auth object that contains an email verified property of true or false. We could also see when a user signed up, if they're anonymous, if they have a phone number, to compose all kinds of other useful rules. But another common thing you'll need in your rules is to know what the existing data looks like as compared to the incoming data. To get the existing data, you can use the resource keyword followed by data, but try not to confuse that with request resource data. I recommend writing functions as existing data and incoming data just to make this very explicit and clear because these are easy to mix up. And it's really important that you don't mess up your back end rules. So when might you need existing data? A common use case would be if you have a certain document that maybe gets locked after it's published. If we jump up to the products path, we can look at the existing product document, and we'll make sure that it's not locked before running the update.
data
data analysis
data management
cryptocurrency
databases
email
documentation
Checking existing data is usually most important when your users can control whether or not a document should be modified at some point. When it comes to incoming data, there's all kinds of validation rules that we can apply to our underlying data structure. Let's imagine that our product document must have a price of greater than $10.
data
data management
data analysis
So if the user tries to send an update where the price is less than $10, we're going to cause this update to fail.
So looking at the incoming data is very important for maintaining the integrity of your database. Now switching gears, in episode 75, I talked about role based user authorization, And I want to revisit some of those concepts now. In that lesson, we saved information about the user's role on their user document, And we need to read that document when applying a rule to various parts of the database. This document is not going to be available on the user object, So we're going to need a different mechanism to read the document whenever needed. The rules environment has a get keyword that will read a document by pointing to a specific path. We need to use the absolute path, so this gets pretty verbose.
databases
data management
documentation
data
security
But if you put it into a function, then it's not too bad. We write out the path like normal and then use dollar sign parenthesis and interpolate the request auth user ID. This gives us the current user's document in Firestore, which contains information about their authorized roles. Now let's use this function to authorize a user to update product. We can read the document, and then we have an object of roles. So we can get all of the keys from that object by calling keys on it.
Then has any will check if the user has any of these authorized roles such as editor or admin. You could also use has all to make sure that the user has all of these roles. At the end of the day, that gives you a very simple solution for implementing role based user authorization. Another interesting thing to think about is how time impacts your database security. A common scenario is to throttle the amount of data that a user can create during a certain duration of time.
databases
data management
customer support
We can get the current time of any request by just calling request time. Then there's a duration helper that we can use to operate on the time stamp. For example, we wanna make sure the request time comes after the created at timestamp plus a duration of 60 seconds. In other words, the user can only write to a product document every 60 seconds. So duration just allows you to add a number of seconds, minutes, or hours numbers and compare them to a Firestore timestamp.
I'm going to go ahead and wrap up there. Hopefully, this helps you avoid becoming the next major data breach victim. If this video helped you, please like and subscribe. And if you're ready to take things to the next level, consider becoming a pro subscriber at angularfirebase.com. You'll get a whole bunch of exclusive content and direct access to chat with me on Slack.
youtube
Thanks for watching, and I'll see you soon.
I can hack into your Firebase app in about 10 seconds, then steal and delete all your data, assuming you failed to set up back end security rules. Allow me to show you. All I have to do is pull up an app and go into the Chrome developer tools under the network tab, then find a request coming from Firebase, open up the headers, and find the corresponding project ID. From there, I can just go to the landline, and run a curl delete request to the Firebase REST API and start wiping out this app's database. If you deploy a Firebase app without back end rules, Anybody can do this to your app as well. Today, I'm going to show you how to keep your Firestore database secure by writing expressive, easy to understand back end security rules. If you're new here, like and subscribe, and grab the source code from angularfirebase.com. To get started, you'll wanna go to the Firestore database, and then open up the rules tab. Rules are defined in their own special language that somewhat resembles JavaScript. Currently, the arrow's pointing to the root of our database, and all of Our rules logic will be defined inside this block. Then we use the match keyword to point to paths in our database where we want to apply rules. In this case, we're using the equals star star to match every single document in the database. Then we use the allow keyword followed by the operation that we wanna set a rule for, in this case, read or write. If we leave it blank, it's going to allow those operations, or we can write an expression that returns true or false to apply some actual rule logic to this route. For example, if we wanted to completely lock down all documents, we could say if false, and then nobody could read or write from the client side. If I try to query a collection from my front end app, I'm going to get an error in the browser console. So cool. Now our app's 100% secure, but that's a little too secure. Now we're going to write some rules that perform logic based on user authentication, the underlying data, the incoming request time, etcetera. First, let's look at the various types of requests that we can lock down. First, we have get, which would apply to reading a specific document. Then list would apply to a collection query. So those are our read rules. Then our write rules, we have create, which applies to creating new data, update for modifying existing data, and delete for removing data. In addition, we could say allow read, which would just combine get and list, or we could also say, allow write, which would combine create, update, delete into a single rule. So that's how we scope rules to specific operations. Now I wanna show you how to point to the actual data that we want to apply these rules to. The first thing to make note of is the equal star star. That special syntax is going to tell your rules to cascade down to all subcollections and anything nested under that path. That's useful when you have a rule that's applied to many different collections, such as verifying a user is authenticated. You can also make your rules very specific. For example, we can point to a specific document using the document ID. All you have to do is hard code it directly into the path and then write the corresponding rules. That can be useful sometimes, but the single most useful matcher is the wildcard ID. Instead of hard coding the ID, we just add brackets with a variable name that we can evaluate as the ID at runtime. By setting product ID right here, we can use that as a variable that represents the document ID. Then we can evaluate that ID in the actual allow statement, which we'll see in action here throughout the lesson. And the wildcard is by far the most common matcher that you're going to be using throughout your back end rules. I'd also like to point out that your rules are secure by default. So unless you explicitly allow an operation, it's going to be blocked by Firestore. In my demo here, I'm allowing the user to read the documents, but only delete a document if they are logged in. We can figure that out by looking at the request object that is built into the rules environment. The request object is very important, and for one, it gives us information about the current user. To simply see if a user is logged in, we can see if the request auth does not equal null. So that's pretty cool, but the thing I don't like about it is that it doesn't read very well. That brings me to my favorite part of Firestore rules and that's the ability to write your own custom functions. Functions allow us to repurpose our rules in a way that's dry and readable. So instead of writing if request auth does not equal null a whole bunch of times, I'm going to write a function called is signed in. The function will just return the request auth does not equal null statement. Then we can replace it on the allow line, and now it just reads like a plain English sentence that anybody can understand. Allow delete if signed in. Pretty simple. My goal from here on out is to give you a bunch of helper functions that you can easily reuse in your own project. The next thing we might wanna do is determine whether or not a user is the owner of a certain document. A good example might be a user profile where all users can read other users' profiles, but only the owner can write to it. So here, we're matching the user's user ID document and allowing read if they're signed in, but only allowing write if they're the owner user. And notice how we're passing the wildcard variable to the argument in our function. For this to work, it's important that the document ID matches the user ID. And if you've seen my authentication tutorials, you'll know how to do that. Then when defining this function, all we have to do is look at the auth user ID on the request and compare it to the user ID on that document that we passed as the argument. If the IDs are the same, then we know that the current user is the owner of this document. And we can actually get a lot more sophisticated. Let's say we want to determine whether or not a user has a verified email. I'm going to use an and statement to chain another function to our rule. With an and statement, both functions will need to return true for this rule to pass. You can also chain things together with an or statement to check if only one of the conditions is true. But in this case, we wanna make sure the user's the owner and that they have a verified email. Like I mentioned before, the request object has all kinds of useful information. In this case, we're going to look at the token on the auth object that contains an email verified property of true or false. We could also see when a user signed up, if they're anonymous, if they have a phone number, to compose all kinds of other useful rules. But another common thing you'll need in your rules is to know what the existing data looks like as compared to the incoming data. To get the existing data, you can use the resource keyword followed by data, but try not to confuse that with request resource data. I recommend writing functions as existing data and incoming data just to make this very explicit and clear because these are easy to mix up. And it's really important that you don't mess up your back end rules. So when might you need existing data? A common use case would be if you have a certain document that maybe gets locked after it's published. If we jump up to the products path, we can look at the existing product document, and we'll make sure that it's not locked before running the update. Checking existing data is usually most important when your users can control whether or not a document should be modified at some point. When it comes to incoming data, there's all kinds of validation rules that we can apply to our underlying data structure. Let's imagine that our product document must have a price of greater than $10. So if the user tries to send an update where the price is less than $10, we're going to cause this update to fail. So looking at the incoming data is very important for maintaining the integrity of your database. Now switching gears, in episode 75, I talked about role based user authorization, And I want to revisit some of those concepts now. In that lesson, we saved information about the user's role on their user document, And we need to read that document when applying a rule to various parts of the database. This document is not going to be available on the user object, So we're going to need a different mechanism to read the document whenever needed. The rules environment has a get keyword that will read a document by pointing to a specific path. We need to use the absolute path, so this gets pretty verbose. But if you put it into a function, then it's not too bad. We write out the path like normal and then use dollar sign parenthesis and interpolate the request auth user ID. This gives us the current user's document in Firestore, which contains information about their authorized roles. Now let's use this function to authorize a user to update product. We can read the document, and then we have an object of roles. So we can get all of the keys from that object by calling keys on it. Then has any will check if the user has any of these authorized roles such as editor or admin. You could also use has all to make sure that the user has all of these roles. At the end of the day, that gives you a very simple solution for implementing role based user authorization. Another interesting thing to think about is how time impacts your database security. A common scenario is to throttle the amount of data that a user can create during a certain duration of time. We can get the current time of any request by just calling request time. Then there's a duration helper that we can use to operate on the time stamp. For example, we wanna make sure the request time comes after the created at timestamp plus a duration of 60 seconds. In other words, the user can only write to a product document every 60 seconds. So duration just allows you to add a number of seconds, minutes, or hours numbers and compare them to a Firestore timestamp. I'm going to go ahead and wrap up there. Hopefully, this helps you avoid becoming the next major data breach victim. If this video helped you, please like and subscribe. And if you're ready to take things to the next level, consider becoming a pro subscriber at angularfirebase.com. You'll get a whole bunch of exclusive content and direct access to chat with me on Slack. Thanks for watching, and I'll see you soon.
I
i
can
can
hack
hack
into
into
your
your
Firebase
firebase
app
app
in
in
about
about
10
10
seconds,
seconds
then
then
steal
steal
and
and
delete
delete
all
all
your
your
data,
data
assuming
assuming
you
you
failed
failed
to
to
set
set
up
up
back
back
end
end
security
security
rules.
rules
Allow
allow
me
me
to
to
show
show
you.
you
All
all
I
i
have
have
to
to
do
do
is
is
pull
pull
up
up
an
an
app
app
and
and
go
go
into
into
the
the
Chrome
chrome
developer
developer
tools
tools
under
under
the
the
network
network
tab,
tab
then
then
find
find
a
a
request
request
coming
coming
from
from
Firebase,
firebase
open
open
up
up
the
the
headers,
headers
and
and
find
find
the
the
corresponding
corresponding
project
project
ID.
id
From
from
there,
there
I
i
can
can
just
just
go
go
to
to
the
the
landline,
landline
and
and
run
run
a
a
curl
curl
delete
delete
request
request
to
to
the
the
Firebase
firebase
REST
rest
API
api
and
and
start
start
wiping
wiping
out
out
this
this
app's
app's
database.
database
If
if
you
you
deploy
deploy
a
a
Firebase
firebase
app
app
without
without
back
back
end
end
rules,
rules
Anybody
anybody
can
can
do
do
this
this
to
to
your
your
app
app
as
as
well.
well
Today,
today
I'm
i'm
going
going
to
to
show
show
you
you
how
how
to
to
keep
keep
your
your
Firestore
firestore
database
database
secure
secure
by
by
writing
writing
expressive,
expressive
easy
easy
to
to
understand
understand
back
back
end
end
security
security
rules.
rules
If
if
you're
you're
new
new
here,
here
like
like
and
and
subscribe,
subscribe
and
and
grab
grab
the
the
source
source
code
code
from
from
angularfirebase.com.
angularfirebase.com
To
to
get
get
started,
started
you'll
you'll
wanna
wanna
go
go
to
to
the
the
Firestore
firestore
database,
database
and
and
then
then
open
open
up
up
the
the
rules
rules
tab.
tab
Rules
rules
are
are
defined
defined
in
in
their
their
own
own
special
special
language
language
that
that
somewhat
somewhat
resembles
resembles
JavaScript.
javascript
Currently,
currently
the
the
arrow's
arrow's
pointing
pointing
to
to
the
the
root
root
of
of
our
our
database,
database
and
and
all
all
of
of
Our
our
rules
rules
logic
logic
will
will
be
be
defined
defined
inside
inside
this
this
block.
block
Then
then
we
we
use
use
the
the
match
match
keyword
keyword
to
to
point
point
to
to
paths
paths
in
in
our
our
database
database
where
where
we
we
want
want
to
to
apply
apply
rules.
rules
In
in
this
this
case,
case
we're
we're
using
using
the
the
equals
equals
star
star
star
star
to
to
match
match
every
every
single
single
document
document
in
in
the
the
database.
database
Then
then
we
we
use
use
the
the
allow
allow
keyword
keyword
followed
followed
by
by
the
the
operation
operation
that
that
we
we
wanna
wanna
set
set
a
a
rule
rule
for,
for
in
in
this
this
case,
case
read
read
or
or
write.
write
If
if
we
we
leave
leave
it
it
blank,
blank
it's
it's
going
going
to
to
allow
allow
those
those
operations,
operations
or
or
we
we
can
can
write
write
an
an
expression
expression
that
that
returns
returns
true
true
or
or
false
false
to
to
apply
apply
some
some
actual
actual
rule
rule
logic
logic
to
to
this
this
route.
route
For
for
example,
example
if
if
we
we
wanted
wanted
to
to
completely
completely
lock
lock
down
down
all
all
documents,
documents
we
we
could
could
say
say
if
if
false,
false
and
and
then
then
nobody
nobody
could
could
read
read
or
or
write
write
from
from
the
the
client
client
side.
side
If
if
I
i
try
try
to
to
query
query
a
a
collection
collection
from
from
my
my
front
front
end
end
app,
app
I'm
i'm
going
going
to
to
get
get
an
an
error
error
in
in
the
the
browser
browser
console.
console
So
so
cool.
cool
Now
now
our
our
app's
app's
100%
100%
secure,
secure
but
but
that's
that's
a
a
little
little
too
too
secure.
secure
Now
now
we're
we're
going
going
to
to
write
write
some
some
rules
rules
that
that
perform
perform
logic
logic
based
based
on
on
user
user
authentication,
authentication
the
the
underlying
underlying
data,
data
the
the
incoming
incoming
request
request
time,
time
etcetera.
etcetera
First,
first
let's
let's
look
look
at
at
the
the
various
various
types
types
of
of
requests
requests
that
that
we
we
can
can
lock
lock
down.
down
First,
first
we
we
have
have
get,
get
which
which
would
would
apply
apply
to
to
reading
reading
a
a
specific
specific
document.
document
Then
then
list
list
would
would
apply
apply
to
to
a
a
collection
collection
query.
query
So
so
those
those
are
are
our
our
read
read
rules.
rules
Then
then
our
our
write
write
rules,
rules
we
we
have
have
create,
create
which
which
applies
applies
to
to
creating
creating
new
new
data,
data
update
update
for
for
modifying
modifying
existing
existing
data,
data
and
and
delete
delete
for
for
removing
removing
data.
data
In
in
addition,
addition
we
we
could
could
say
say
allow
allow
read,
read
which
which
would
would
just
just
combine
combine
get
get
and
and
list,
list
or
or
we
we
could
could
also
also
say,
say
allow
allow
write,
write
which
which
would
would
combine
combine
create,
create
update,
update
delete
delete
into
into
a
a
single
single
rule.
rule
So
so
that's
that's
how
how
we
we
scope
scope
rules
rules
to
to
specific
specific
operations.
operations
Now
now
I
i
wanna
wanna
show
show
you
you
how
how
to
to
point
point
to
to
the
the
actual
actual
data
data
that
that
we
we
want
want
to
to
apply
apply
these
these
rules
rules
to.
to
The
the
first
first
thing
thing
to
to
make
make
note
note
of
of
is
is
the
the
equal
equal
star
star
star.
star
That
that
special
special
syntax
syntax
is
is
going
going
to
to
tell
tell
your
your
rules
rules
to
to
cascade
cascade
down
down
to
to
all
all
subcollections
subcollections
and
and
anything
anything
nested
nested
under
under
that
that
path.
path
That's
that's
useful
useful
when
when
you
you
have
have
a
a
rule
rule
that's
that's
applied
applied
to
to
many
many
different
different
collections,
collections
such
such
as
as
verifying
verifying
a
a
user
user
is
is
authenticated.
authenticated
You
you
can
can
also
also
make
make
your
your
rules
rules
very
very
specific.
specific
For
for
example,
example
we
we
can
can
point
point
to
to
a
a
specific
specific
document
document
using
using
the
the
document
document
ID.
id
All
all
you
you
have
have
to
to
do
do
is
is
hard
hard
code
code
it
it
directly
directly
into
into
the
the
path
path
and
and
then
then
write
write
the
the
corresponding
corresponding
rules.
rules
That
that
can
can
be
be
useful
useful
sometimes,
sometimes
but
but
the
the
single
single
most
most
useful
useful
matcher
matcher
is
is
the
the
wildcard
wildcard
ID.
id
Instead
instead
of
of
hard
hard
coding
coding
the
the
ID,
id
we
we
just
just
add
add
brackets
brackets
with
with
a
a
variable
variable
name
name
that
that
we
we
can
can
evaluate
evaluate
as
as
the
the
ID
id
at
at
runtime.
runtime
By
by
setting
setting
product
product
ID
id
right
right
here,
here
we
we
can
can
use
use
that
that
as
as
a
a
variable
variable
that
that
represents
represents
the
the
document
document
ID.
id
Then
then
we
we
can
can
evaluate
evaluate
that
that
ID
id
in
in
the
the
actual
actual
allow
allow
statement,
statement
which
which
we'll
we'll
see
see
in
in
action
action
here
here
throughout
throughout
the
the
lesson.
lesson
And
and
the
the
wildcard
wildcard
is
is
by
by
far
far
the
the
most
most
common
common
matcher
matcher
that
that
you're
you're
going
going
to
to
be
be
using
using
throughout
throughout
your
your
back
back
end
end
rules.
rules
I'd
i'd
also
also
like
like
to
to
point
point
out
out
that
that
your
your
rules
rules
are
are
secure
secure
by
by
default.
default
So
so
unless
unless
you
you
explicitly
explicitly
allow
allow
an
an
operation,
operation
it's
it's
going
going
to
to
be
be
blocked
blocked
by
by
Firestore.
firestore
In
in
my
my
demo
demo
here,
here
I'm
i'm
allowing
allowing
the
the
user
user
to
to
read
read
the
the
documents,
documents
but
but
only
only
delete
delete
a
a
document
document
if
if
they
they
are
are
logged
logged
in.
in
We
we
can
can
figure
figure
that
that
out
out
by
by
looking
looking
at
at
the
the
request
request
object
object
that
that
is
is
built
built
into
into
the
the
rules
rules
environment.
environment
The
the
request
request
object
object
is
is
very
very
important,
important
and
and
for
for
one,
one
it
it
gives
gives
us
us
information
information
about
about
the
the
current
current
user.
user
To
to
simply
simply
see
see
if
if
a
a
user
user
is
is
logged
logged
in,
in
we
we
can
can
see
see
if
if
the
the
request
request
auth
auth
does
does
not
not
equal
equal
null.
null
So
so
that's
that's
pretty
pretty
cool,
cool
but
but
the
the
thing
thing
I
i
don't
don't
like
like
about
about
it
it
is
is
that
that
it
it
doesn't
doesn't
read
read
very
very
well.
well
That
that
brings
brings
me
me
to
to
my
my
favorite
favorite
part
part
of
of
Firestore
firestore
rules
rules
and
and
that's
that's
the
the
ability
ability
to
to
write
write
your
your
own
own
custom
custom
functions.
functions
Functions
functions
allow
allow
us
us
to
to
repurpose
repurpose
our
our
rules
rules
in
in
a
a
way
way
that's
that's
dry
dry
and
and
readable.
readable
So
so
instead
instead
of
of
writing
writing
if
if
request
request
auth
auth
does
does
not
not
equal
equal
null
null
a
a
whole
whole
bunch
bunch
of
of
times,
times
I'm
i'm
going
going
to
to
write
write
a
a
function
function
called
called
is
is
signed
signed
in.
in
The
the
function
function
will
will
just
just
return
return
the
the
request
request
auth
auth
does
does
not
not
equal
equal
null
null
statement.
statement
Then
then
we
we
can
can
replace
replace
it
it
on
on
the
the
allow
allow
line,
line
and
and
now
now
it
it
just
just
reads
reads
like
like
a
a
plain
plain
English
english
sentence
sentence
that
that
anybody
anybody
can
can
understand.
understand
Allow
allow
delete
delete
if
if
signed
signed
in.
in
Pretty
pretty
simple.
simple
My
my
goal
goal
from
from
here
here
on
on
out
out
is
is
to
to
give
give
you
you
a
a
bunch
bunch
of
of
helper
helper
functions
functions
that
that
you
you
can
can
easily
easily
reuse
reuse
in
in
your
your
own
own
project.
project
The
the
next
next
thing
thing
we
we
might
might
wanna
wanna
do
do
is
is
determine
determine
whether
whether
or
or
not
not
a
a
user
user
is
is
the
the
owner
owner
of
of
a
a
certain
certain
document.
document
A
a
good
good
example
example
might
might
be
be
a
a
user
user
profile
profile
where
where
all
all
users
users
can
can
read
read
other
other
users'
users'
profiles,
profiles
but
but
only
only
the
the
owner
owner
can
can
write
write
to
to
it.
it
So
so
here,
here
we're
we're
matching
matching
the
the
user's
user's
user
user
ID
id
document
document
and
and
allowing
allowing
read
read
if
if
they're
they're
signed
signed
in,
in
but
but
only
only
allowing
allowing
write
write
if
if
they're
they're
the
the
owner
owner
user.
user
And
and
notice
notice
how
how
we're
we're
passing
passing
the
the
wildcard
wildcard
variable
variable
to
to
the
the
argument
argument
in
in
our
our
function.
function
For
for
this
this
to
to
work,
work
it's
it's
important
important
that
that
the
the
document
document
ID
id
matches
matches
the
the
user
user
ID.
id
And
and
if
if
you've
you've
seen
seen
my
my
authentication
authentication
tutorials,
tutorials
you'll
you'll
know
know
how
how
to
to
do
do
that.
that
Then
then
when
when
defining
defining
this
this
function,
function
all
all
we
we
have
have
to
to
do
do
is
is
look
look
at
at
the
the
auth
auth
user
user
ID
id
on
on
the
the
request
request
and
and
compare
compare
it
it
to
to
the
the
user
user
ID
id
on
on
that
that
document
document
that
that
we
we
passed
passed
as
as
the
the
argument.
argument
If
if
the
the
IDs
ids
are
are
the
the
same,
same
then
then
we
we
know
know
that
that
the
the
current
current
user
user
is
is
the
the
owner
owner
of
of
this
this
document.
document
And
and
we
we
can
can
actually
actually
get
get
a
a
lot
lot
more
more
sophisticated.
sophisticated
Let's
let's
say
say
we
we
want
want
to
to
determine
determine
whether
whether
or
or
not
not
a
a
user
user
has
has
a
a
verified
verified
email.
email
I'm
i'm
going
going
to
to
use
use
an
an
and
and
statement
statement
to
to
chain
chain
another
another
function
function
to
to
our
our
rule.
rule
With
with
an
an
and
and
statement,
statement
both
both
functions
functions
will
will
need
need
to
to
return
return
true
true
for
for
this
this
rule
rule
to
to
pass.
pass
You
you
can
can
also
also
chain
chain
things
things
together
together
with
with
an
an
or
or
statement
statement
to
to
check
check
if
if
only
only
one
one
of
of
the
the
conditions
conditions
is
is
true.
true
But
but
in
in
this
this
case,
case
we
we
wanna
wanna
make
make
sure
sure
the
the
user's
user's
the
the
owner
owner
and
and
that
that
they
they
have
have
a
a
verified
verified
email.
email
Like
like
I
i
mentioned
mentioned
before,
before
the
the
request
request
object
object
has
has
all
all
kinds
kinds
of
of
useful
useful
information.
information
In
in
this
this
case,
case
we're
we're
going
going
to
to
look
look
at
at
the
the
token
token
on
on
the
the
auth
auth
object
object
that
that
contains
contains
an
an
email
email
verified
verified
property
property
of
of
true
true
or
or
false.
false
We
we
could
could
also
also
see
see
when
when
a
a
user
user
signed
signed
up,
up
if
if
they're
they're
anonymous,
anonymous
if
if
they
they
have
have
a
a
phone
phone
number,
number
to
to
compose
compose
all
all
kinds
kinds
of
of
other
other
useful
useful
rules.
rules
But
but
another
another
common
common
thing
thing
you'll
you'll
need
need
in
in
your
your
rules
rules
is
is
to
to
know
know
what
what
the
the
existing
existing
data
data
looks
looks
like
like
as
as
compared
compared
to
to
the
the
incoming
incoming
data.
data
To
to
get
get
the
the
existing
existing
data,
data
you
you
can
can
use
use
the
the
resource
resource
keyword
keyword
followed
followed
by
by
data,
data
but
but
try
try
not
not
to
to
confuse
confuse
that
that
with
with
request
request
resource
resource
data.
data
I
i
recommend
recommend
writing
writing
functions
functions
as
as
existing
existing
data
data
and
and
incoming
incoming
data
data
just
just
to
to
make
make
this
this
very
very
explicit
explicit
and
and
clear
clear
because
because
these
these
are
are
easy
easy
to
to
mix
mix
up.
up
And
and
it's
it's
really
really
important
important
that
that
you
you
don't
don't
mess
mess
up
up
your
your
back
back
end
end
rules.
rules
So
so
when
when
might
might
you
you
need
need
existing
existing
data?
data
A
a
common
common
use
use
case
case
would
would
be
be
if
if
you
you
have
have
a
a
certain
certain
document
document
that
that
maybe
maybe
gets
gets
locked
locked
after
after
it's
it's
published.
published
If
if
we
we
jump
jump
up
up
to
to
the
the
products
products
path,
path
we
we
can
can
look
look
at
at
the
the
existing
existing
product
product
document,
document
and
and
we'll
we'll
make
make
sure
sure
that
that
it's
it's
not
not
locked
locked
before
before
running
running
the
the
update.
update
Checking
checking
existing
existing
data
data
is
is
usually
usually
most
most
important
important
when
when
your
your
users
users
can
can
control
control
whether
whether
or
or
not
not
a
a
document
document
should
should
be
be
modified
modified
at
at
some
some
point.
point
When
when
it
it
comes
comes
to
to
incoming
incoming
data,
data
there's
there's
all
all
kinds
kinds
of
of
validation
validation
rules
rules
that
that
we
we
can
can
apply
apply
to
to
our
our
underlying
underlying
data
data
structure.
structure
Let's
let's
imagine
imagine
that
that
our
our
product
product
document
document
must
must
have
have
a
a
price
price
of
of
greater
greater
than
than
$10.
$10
So
so
if
if
the
the
user
user
tries
tries
to
to
send
send
an
an
update
update
where
where
the
the
price
price
is
is
less
less
than
than
$10,
$10
we're
we're
going
going
to
to
cause
cause
this
this
update
update
to
to
fail.
fail
So
so
looking
looking
at
at
the
the
incoming
incoming
data
data
is
is
very
very
important
important
for
for
maintaining
maintaining
the
the
integrity
integrity
of
of
your
your
database.
database
Now
now
switching
switching
gears,
gears
in
in
episode
episode
75,
75
I
i
talked
talked
about
about
role
role
based
based
user
user
authorization,
authorization
And
and
I
i
want
want
to
to
revisit
revisit
some
some
of
of
those
those
concepts
concepts
now.
now
In
in
that
that
lesson,
lesson
we
we
saved
saved
information
information
about
about
the
the
user's
user's
role
role
on
on
their
their
user
user
document,
document
And
and
we
we
need
need
to
to
read
read
that
that
document
document
when
when
applying
applying
a
a
rule
rule
to
to
various
various
parts
parts
of
of
the
the
database.
database
This
this
document
document
is
is
not
not
going
going
to
to
be
be
available
available
on
on
the
the
user
user
object,
object
So
so
we're
we're
going
going
to
to
need
need
a
a
different
different
mechanism
mechanism
to
to
read
read
the
the
document
document
whenever
whenever
needed.
needed
The
the
rules
rules
environment
environment
has
has
a
a
get
get
keyword
keyword
that
that
will
will
read
read
a
a
document
document
by
by
pointing
pointing
to
to
a
a
specific
specific
path.
path
We
we
need
need
to
to
use
use
the
the
absolute
absolute
path,
path
so
so
this
this
gets
gets
pretty
pretty
verbose.
verbose
But
but
if
if
you
you
put
put
it
it
into
into
a
a
function,
function
then
then
it's
it's
not
not
too
too
bad.
bad
We
we
write
write
out
out
the
the
path
path
like
like
normal
normal
and
and
then
then
use
use
dollar
dollar
sign
sign
parenthesis
parenthesis
and
and
interpolate
interpolate
the
the
request
request
auth
auth
user
user
ID.
id
This
this
gives
gives
us
us
the
the
current
current
user's
user's
document
document
in
in
Firestore,
firestore
which
which
contains
contains
information
information
about
about
their
their
authorized
authorized
roles.
roles
Now
now
let's
let's
use
use
this
this
function
function
to
to
authorize
authorize
a
a
user
user
to
to
update
update
product.
product
We
we
can
can
read
read
the
the
document,
document
and
and
then
then
we
we
have
have
an
an
object
object
of
of
roles.
roles
So
so
we
we
can
can
get
get
all
all
of
of
the
the
keys
keys
from
from
that
that
object
object
by
by
calling
calling
keys
keys
on
on
it.
it
Then
then
has
has
any
any
will
will
check
check
if
if
the
the
user
user
has
has
any
any
of
of
these
these
authorized
authorized
roles
roles
such
such
as
as
editor
editor
or
or
admin.
admin
You
you
could
could
also
also
use
use
has
has
all
all
to
to
make
make
sure
sure
that
that
the
the
user
user
has
has
all
all
of
of
these
these
roles.
roles
At
at
the
the
end
end
of
of
the
the
day,
day
that
that
gives
gives
you
you
a
a
very
very
simple
simple
solution
solution
for
for
implementing
implementing
role
role
based
based
user
user
authorization.
authorization
Another
another
interesting
interesting
thing
thing
to
to
think
think
about
about
is
is
how
how
time
time
impacts
impacts
your
your
database
database
security.
security
A
a
common
common
scenario
scenario
is
is
to
to
throttle
throttle
the
the
amount
amount
of
of
data
data
that
that
a
a
user
user
can
can
create
create
during
during
a
a
certain
certain
duration
duration
of
of
time.
time
We
we
can
can
get
get
the
the
current
current
time
time
of
of
any
any
request
request
by
by
just
just
calling
calling
request
request
time.
time
Then
then
there's
there's
a
a
duration
duration
helper
helper
that
that
we
we
can
can
use
use
to
to
operate
operate
on
on
the
the
time
time
stamp.
stamp
For
for
example,
example
we
we
wanna
wanna
make
make
sure
sure
the
the
request
request
time
time
comes
comes
after
after
the
the
created
created
at
at
timestamp
timestamp
plus
plus
a
a
duration
duration
of
of
60
60
seconds.
seconds
In
in
other
other
words,
words
the
the
user
user
can
can
only
only
write
write
to
to
a
a
product
product
document
document
every
every
60
60
seconds.
seconds
So
so
duration
duration
just
just
allows
allows
you
you
to
to
add
add
a
a
number
number
of
of
seconds,
seconds
minutes,
minutes
or
or
hours
hours
numbers
numbers
and
and
compare
compare
them
them
to
to
a
a
Firestore
firestore
timestamp.
timestamp
I'm
i'm
going
going
to
to
go
go
ahead
ahead
and
and
wrap
wrap
up
up
there.
there
Hopefully,
hopefully
this
this
helps
helps
you
you
avoid
avoid
becoming
becoming
the
the
next
next
major
major
data
data
breach
breach
victim.
victim
If
if
this
this
video
video
helped
helped
you,
you
please
please
like
like
and
and
subscribe.
subscribe
And
and
if
if
you're
you're
ready
ready
to
to
take
take
things
things
to
to
the
the
next
next
level,
level
consider
consider
becoming
becoming
a
a
pro
pro
subscriber
subscriber
at
at
angularfirebase.com.
angularfirebase.com
You'll
you'll
get
get
a
a
whole
whole
bunch
bunch
of
of
exclusive
exclusive
content
content
and
and
direct
direct
access
access
to
to
chat
chat
with
with
me
me
on
on
Slack.
slack
Thanks
thanks
for
for
watching,
watching
and
and
I'll
i'll
see
see
you
you
soon.
soon
success
The speakers explain how to keep Fir POS database secure by writing expressive back-end security rules and the use of wildcard ID to match records. They also show how to create and update rules, point to specific data, and read existing data and incoming data. The speakers stress the importance of checking existing data and the use of resource keywords to make it clear. They also explain how to authorize users to update a document, match user IDs, and read data from the existing and incoming data. Viewers are encouraged to subscribe to the video and offer exclusive content and direct access to chat with them on Slack.
Firestore Security Rules - How to Hack a Firebase App
I can hack into your Firebase app in about 10 seconds, then steal and delete all your data, assuming you failed to set up back end security rules. Allow me to show you. All I have to do is pull up an app and go into the Chrome developer tools under the network tab, then find a request coming from Firebase, open up the headers, and find the corresponding project ID. From there, I can just go to the landline, and run a curl delete request to the Firebase REST API and start wiping out this app's database. If you deploy a Firebase app without back end rules, Anybody can do this to your app as well. Today, I'm going to show you how to keep your Firestore database secure by writing expressive, easy to understand back end security rules. If you're new here, like and subscribe, and grab the source code from angularfirebase.com. To get started, you'll wanna go to the Firestore database, and then open up the rules tab. Rules are defined in their own special language that somewhat resembles JavaScript. Currently, the arrow's pointing to the root of our database, and all of Our rules logic will be defined inside this block. Then we use the match keyword to point to paths in our database where we want to apply rules. In this case, we're using the equals star star to match every single document in the database. Then we use the allow keyword followed by the operation that we wanna set a rule for, in this case, read or write. If we leave it blank, it's going to allow those operations, or we can write an expression that returns true or false to apply some actual rule logic to this route. For example, if we wanted to completely lock down all documents, we could say if false, and then nobody could read or write from the client side. If I try to query a collection from my front end app, I'm going to get an error in the browser console. So cool. Now our app's 100% secure, but that's a little too secure. Now we're going to write some rules that perform logic based on user authentication, the underlying data, the incoming request time, etcetera. First, let's look at the various types of requests that we can lock down. First, we have get, which would apply to reading a specific document. Then list would apply to a collection query. So those are our read rules. Then our write rules, we have create, which applies to creating new data, update for modifying existing data, and delete for removing data. In addition, we could say allow read, which would just combine get and list, or we could also say, allow write, which would combine create, update, delete into a single rule. So that's how we scope rules to specific operations. Now I wanna show you how to point to the actual data that we want to apply these rules to. The first thing to make note of is the equal star star. That special syntax is going to tell your rules to cascade down to all subcollections and anything nested under that path. That's useful when you have a rule that's applied to many different collections, such as verifying a user is authenticated. You can also make your rules very specific. For example, we can point to a specific document using the document ID. All you have to do is hard code it directly into the path and then write the corresponding rules. That can be useful sometimes, but the single most useful matcher is the wildcard ID. Instead of hard coding the ID, we just add brackets with a variable name that we can evaluate as the ID at runtime. By setting product ID right here, we can use that as a variable that represents the document ID. Then we can evaluate that ID in the actual allow statement, which we'll see in action here throughout the lesson. And the wildcard is by far the most common matcher that you're going to be using throughout your back end rules. I'd also like to point out that your rules are secure by default. So unless you explicitly allow an operation, it's going to be blocked by Firestore. In my demo here, I'm allowing the user to read the documents, but only delete a document if they are logged in. We can figure that out by looking at the request object that is built into the rules environment. The request object is very important, and for one, it gives us information about the current user. To simply see if a user is logged in, we can see if the request auth does not equal null. So that's pretty cool, but the thing I don't like about it is that it doesn't read very well. That brings me to my favorite part of Firestore rules and that's the ability to write your own custom functions. Functions allow us to repurpose our rules in a way that's dry and readable. So instead of writing if request auth does not equal null a whole bunch of times, I'm going to write a function called is signed in. The function will just return the request auth does not equal null statement. Then we can replace it on the allow line, and now it just reads like a plain English sentence that anybody can understand. Allow delete if signed in. Pretty simple. My goal from here on out is to give you a bunch of helper functions that you can easily reuse in your own project. The next thing we might wanna do is determine whether or not a user is the owner of a certain document. A good example might be a user profile where all users can read other users' profiles, but only the owner can write to it. So here, we're matching the user's user ID document and allowing read if they're signed in, but only allowing write if they're the owner user. And notice how we're passing the wildcard variable to the argument in our function. For this to work, it's important that the document ID matches the user ID. And if you've seen my authentication tutorials, you'll know how to do that. Then when defining this function, all we have to do is look at the auth user ID on the request and compare it to the user ID on that document that we passed as the argument. If the IDs are the same, then we know that the current user is the owner of this document. And we can actually get a lot more sophisticated. Let's say we want to determine whether or not a user has a verified email. I'm going to use an and statement to chain another function to our rule. With an and statement, both functions will need to return true for this rule to pass. You can also chain things together with an or statement to check if only one of the conditions is true. But in this case, we wanna make sure the user's the owner and that they have a verified email. Like I mentioned before, the request object has all kinds of useful information. In this case, we're going to look at the token on the auth object that contains an email verified property of true or false. We could also see when a user signed up, if they're anonymous, if they have a phone number, to compose all kinds of other useful rules. But another common thing you'll need in your rules is to know what the existing data looks like as compared to the incoming data. To get the existing data, you can use the resource keyword followed by data, but try not to confuse that with request resource data. I recommend writing functions as existing data and incoming data just to make this very explicit and clear because these are easy to mix up. And it's really important that you don't mess up your back end rules. So when might you need existing data? A common use case would be if you have a certain document that maybe gets locked after it's published. If we jump up to the products path, we can look at the existing product document, and we'll make sure that it's not locked before running the update. Checking existing data is usually most important when your users can control whether or not a document should be modified at some point. When it comes to incoming data, there's all kinds of validation rules that we can apply to our underlying data structure. Let's imagine that our product document must have a price of greater than $10. So if the user tries to send an update where the price is less than $10, we're going to cause this update to fail. So looking at the incoming data is very important for maintaining the integrity of your database. Now switching gears, in episode 75, I talked about role based user authorization, And I want to revisit some of those concepts now. In that lesson, we saved information about the user's role on their user document, And we need to read that document when applying a rule to various parts of the database. This document is not going to be available on the user object, So we're going to need a different mechanism to read the document whenever needed. The rules environment has a get keyword that will read a document by pointing to a specific path. We need to use the absolute path, so this gets pretty verbose. But if you put it into a function, then it's not too bad. We write out the path like normal and then use dollar sign parenthesis and interpolate the request auth user ID. This gives us the current user's document in Firestore, which contains information about their authorized roles. Now let's use this function to authorize a user to update product. We can read the document, and then we have an object of roles. So we can get all of the keys from that object by calling keys on it. Then has any will check if the user has any of these authorized roles such as editor or admin. You could also use has all to make sure that the user has all of these roles. At the end of the day, that gives you a very simple solution for implementing role based user authorization. Another interesting thing to think about is how time impacts your database security. A common scenario is to throttle the amount of data that a user can create during a certain duration of time. We can get the current time of any request by just calling request time. Then there's a duration helper that we can use to operate on the time stamp. For example, we wanna make sure the request time comes after the created at timestamp plus a duration of 60 seconds. In other words, the user can only write to a product document every 60 seconds. So duration just allows you to add a number of seconds, minutes, or hours numbers and compare them to a Firestore timestamp. I'm going to go ahead and wrap up there. Hopefully, this helps you avoid becoming the next major data breach victim. If this video helped you, please like and subscribe. And if you're ready to take things to the next level, consider becoming a pro subscriber at angularfirebase.com. You'll get a whole bunch of exclusive content and direct access to chat with me on Slack. Thanks for watching, and I'll see you soon.
The speakers explain how to keep Fir POS database secure by writing expressive back-end security rules and the use of wildcard ID to match records. They also show how to create and update rules, point to specific data, and read existing data and incoming data. The speakers stress the importance of checking existing data and the use of resource keywords to make it clear. They also explain how to authorize users to update a document, match user IDs, and read data from the existing and incoming data. Viewers are encouraged to subscribe to the video and offer exclusive content and direct access to chat with them on Slack.