How to Keep Your Firebase Project Safe and Secure from everyone
Having worked on several open-source Firebase web apps, I feel experienced enough to write this post. If you spot any errors, please let me know.
Why is this necessary?
Here's something many people don't know: Your Firebase config, which you might wonder how to hide, is publicly visible when using Firebase hosting. Don't believe me? Visit any Firebase-hosted site's index page and append "/__/firebase/init.js" (e.g., someapp.web.app/__/firebase/init.js
)—you'll be surprised at what you find. I didn't discover this myself; it's actually documented in the documentation.
Protecting your project
Firestore datastore and Firebase Storage
This is the most critical part of your backend since it likely contains sensitive data that must be protected from malicious actors. How can we secure these components? Here are some steps:
"Start in production mode"
When setting up Firestore and storage, you'll be asked to choose between production mode and test mode. Always select "Production mode" or "Locked mode." This mode implements security rules that block all client access by default. Don't worry though—we can modify these rules later.
Configure security rules
Security rules are very great to have. Using these, you can decide who can have access to data and who cannot. These are written in .rules files and have a similar syntax to JavaScript which can be understood in the docs. Since we used production mode, you will see this particular rule in your security rules:
match /{document=**} {
allow read, write: if false;
}
//Don't remove this rule, or else your project is gone
//and keep this rule at end (this comment is added by me)
Which literally means that no client can access your database and storage, which is sometimes not needed. So we can introduce changes in how we store data.
If you've used Firebase, you know that for every user that signs up for your app, they are assigned a unique ID (or UID). We can store resources based on their UID, for example, in a todo app, one can store user's data in their database at path /todos/users-uid
which makes sense. So in this, we can add a new rule:
match /todos/{uid=\\*\\*} {
allow read, write: if request.auth != null and request.auth.uid == uid;
}
This means that if anyone accesses any path under todos collection, they should be authenticated to the app, and if they try to access a specific UID in the collection, the database will only allow access if they access data under their UID. Do the same for Firebase storage as well.
Use seperate API keys if they are used only for one purpose
Suppose you're developing a CLI for your app (which I am doing) and need an API key for refreshing user JWTs (which I am doing!). Instead of using the same Firebase config API key, create a new one with limited access to only what you need—in this case, the Token Service API. Head over to the Google APIs dashboard and select your project.
- When you're in the dashboard, go to the 'Credentials' section.
- Then in the top app bar, click the "Create Credentials" button. A drop down will appear. Select "API key"
- Then it will show a dialog like this:
- Then that key will appear in the "Keys" section. Then, in that list, find the API key we just created, and then click on the pencil icon.
- Scroll down and you'll find a section "API restrictions". Select the option "Restrict key".
- Then a dropdown will appear with text "Select APIs". Then select the services which you want the API key to be working with. I'll select the Token Service API here.
- Just hit the "Save" button.
Then in the keys' section, you'll find that the API key has a green check on it. It means that it is now secure and you'll bear less damage if it got leaked.
If you need to have your API keys in your repo
If your code is public, and you need your API keys for testing or just to keep everything modular and smooth, encrypt it and then save it. I also use a similar approach in my CLI app, beacuse I need it for CI. You can use any encryption method which uses public-private key pair and then store the encrypted file in your repo and then decrypt it whenever needed.
Respect Firebase server credentials
This is crucial. While we've discussed the general Firebase config used in web apps, server credentials for Admin SDKs are different. These credentials have unlimited access privileges that cannot be restricted. Treat them as sensitive secrets—never share or expose them, as doing so could compromise your entire project.
Ending thoughts
Congratulations on making it this far! Feel free to relax and start coding your project. If you get stuck on any of these steps, don't hesitate to reach out. Have a great day! :)