DOUG STEVENSON: Hey,
Firebase developers. So how do you make sure that
your users can read and write only the data in your
project that they're allowed? I'm sure you
probably already know that you can do this with
Firebase security rules. You can write security
rules to protect the data stored in
Realtime Database, Cloud Firestore, and Cloud Storage. These rules sit between your
client app and the data, making sure that
each of the reads and writes performed
by the client SDK are allowed for the end user. If your client app directly
reads and rights data using any of these
three products, you definitely need to
think about security. I imagine that many of
you are watching today because you got an email
notification saying that your rules aren't secure. Hopefully, by the
end of the series, you'll be able to write rules
that are good for your app. Firebase security rules
provides an expression language that lets you choose how the
end user is allowed to access data in these products. Note that the expression
language is not a full programming language. And there are two
varieties-- they both might feel similar to JavaScript. For Realtime Database, the
rules are formatted using JSON. You can see here
that you identify a node in the database, such as
users, with possible wildcards to match any child node. Then apply conditions
on that to indicate how the data at that node
may be read and written. In this case, we're saying
that anyone can read any child node under users, but
only an authenticated user may write the child
node with the same ID as their own UID assigned
by Firebase authentication. For Cloud Firestore
and Cloud Storage, the syntax is quite different. You have a service
declaration to identify which product is being used-- this is for Cloud Firestore--
one or more match patterns that specify the path, and also
possible wildcards of documents or objects to protect. Nested inside match
patterns there are allow expressions
that determine the specific conditions
for accessing the document or object. The allow keyword
requires that you also provide a method that describes
the nature of the access, which is going to be read or write
or something more granular. This example is similar to the
last one for Realtime Database, and it's saying that all
documents in the user's collection can be
read by anyone, but only an authenticated
user may write the document with the ID that's the
same as their own UID. The expressions
themselves follow the syntax of the common
expression language. While the capabilities
of both rule systems are similar, in this
series, I'm going to spend most of the
time talking about what you can do with a common
expression language used by Firestore and Cloud Storage. But before diving
into that, it's a good idea to wrap your head
around the strategies used to protect data. The way I see it, there are two
main categories of assertions you will want to
express in your rules. First, there are
user-based assertions, which you can apply
only if your app is also using Firebase authentication to
identify and sign in the user. It's very common to use
the unique ID of the user to determine which things
they can read and write. You can also make use of custom
claims granted to the account in order to authorize access. If you're not using
Firebase Auth, you won't be able to write any
rules that depend on knowing something about the user. Authentication is a crucial
part of developing a secure app, and it's my opinion that
you should integrate it into your app as
soon as possible, even if it's just
anonymous authentication, so you can also start writing
good user-based rules. Second, there are
content-based assertions, which you can use
to control access to data based on the
contents of the data itself. So for example, imagine
a Boolean field value in a document that marks it as
publicly readable by everyone. You can use this
Boolean field value in your rules to allow
access to the document. And for write
operations, you can also examine the data about to be
written in order to check it for validity and consistency. Ideally, you'll write rules that
use a combination of these two types of assertions. Rules can get complex, but
after everything is evaluated, it will boil down to
a single Boolean value that either allows
or rejects access. I've said a little
bit about what you can do with
security rules, but it's also important to understand
what you can't do, as well. First, you can't change
the incoming data that's about to be written. While you have access
to the incoming data, your rule can only allow
or deny that right. You can't fix up the
data in some way. If you need to do
something like that, consider using Cloud
Functions to trigger some code that modifies the
data after the write finishes. You also can't check to see if
access is coming from your app or website. Firebase products are
generally intended to be accessible from anywhere
there is internet access, and there are public
REST APIs, as well. It's just not
possible to control the origin of a request
using security rules. One common misunderstanding
about security rules is that they don't act as
query filters on your documents in Firestore. So you can't write
a rule that ever changes the outcome of a query,
other than denying it entirely. I'll say more about
this in another video. Lastly, you can't cross
between products, at least not at the time
of this recording. Cloud Firestore rules only have
access to data in Firestore, and Cloud Storage rules only
have access to object metadata. The only exception to this
is the account information provided by Firebase
authentication for the user that was signed
in prior to making the request. The security rules team is
investigating the possibility of using Firestore document
data in storage rules, but right now it's not possible. As I said before, security
rules don't provide a full programming language. But it is still code. And like all code,
you should test it while you develop to make sure
it works the way you expect. It's possible to write app
code that exercises your rules, but running a special code path
in your app just to test rules isn't very convenient
or efficient. You also don't get a very
helpful error message from the client SDK in the case
that an operation is denied. So to make this easier,
there are two other ways to test your rules. First, it's pretty easy to
get started testing rules in the Firebase console. Realtime Database,
Cloud Firestore, and Cloud Storage all
have a rules simulator in the screen where you
can edit your rules. The simulator lets you
configure a database or storage operation, add authentication
to it if you want, and test that situation
against the rules that you see on the screen. The console will tell you which
line allowed or disallowed access to the data. You can test some
common rules like this, but you should know that
not all situations can be simulated in the console. For testing more
advanced rules, you want to download the Cloud
Firestore local emulator that comes with the Firebase CLI. With the emulator,
you can write code to set up some test
documents, then query those documents to see
if they're correctly allowed or denied by some rules. This emulator is running
the exact same code that evaluates security
rules deployed in production. So you'll know for sure
if they work correctly. It takes some time to
set up the emulator, but this is your overall best
option for serious development. At the time of this recording,
the security rules emulator is in beta, and it's going
to improve in the future. To get started with
that, check the links in the description below. There's a lot to know about
Firebase security rules, and this was just
an introduction. I touched on a bunch
of things today, but I'll dive into
each one of them in more detail in future videos. So be sure to subscribe here to
the Firebase channel on YouTube to find out when the
next one is available. I'll see you then. [MUSIC PLAYING]