To keep your Firebase resources and your users' data secure, follow these guidelines. Not every item will necessarily apply to your requirements, but keep them in mind as you develop your app.
Avoid abusive traffic
Set up monitoring and alerting for backend services
To detect abusive traffic, such as denial-of-service (DOS) attacks, set up monitoring and alerting for Cloud Firestore, Realtime Database, Cloud Storage, and Hosting
If you suspect an attack on your application, reach out to Support as soon as possible to let them know what is happening.
Enable App Check
To help ensure only your apps can access your backend services, enable Firebase App Check for every service that supports it.
Configure your Cloud Functions to scale for normal traffic
Cloud Functions automatically scales to meet your app's demands, but in the event of an attack, this can mean a big bill. To prevent this, you can limit the number of concurrent instances of a function based on normal traffic for your app.
Set up alerting to be notified when the limits are nearly reached
If your service has request spikes, often quotas will kick in, and automatically throttle traffic to your application. Make sure to monitor your Usage and billing dashboard, but you can also set budget alerts on your project to be notified when resource usage is exceeding expectations.
Prevent self-DOSes: test functions locally with the emulators
It can be easy to accidentally DOS yourself while developing Cloud Functions: for example, by creating an infinite trigger-write loop. You can prevent these mistakes from affecting live services by doing your development with the Firebase Local Emulator Suite.
And if you do accidentally DOS yourself, undeploy your function by deleting it
from index.js
and then running
firebase deploy --only functions
Where real-time responsiveness is less important, structure functions defensively
If you don't need to present the result of a function in real time, you can mitigate against abusive traffic by processing the results in batches: publish results to a Pub/Sub topic, and process the results at regular intervals with a scheduled function.
Understand API keys
API keys for Firebase services are not secret
Firebase uses API keys only to identify your app's Firebase project to Firebase services, and not to control access to database or Cloud Storage data, which is done using Firebase Security Rules. For this reason, you do not need to treat API keys for Firebase services as secrets, and you can safely embed them in client code. Learn more about API keys for Firebase.
Set up API key restrictions
As an additional deterrent against an attacker attempting to use your API key to spoof requests, you can add API key restrictions to scope your API keys to your app clients and the APIs you use.
Keep FCM server keys secret
Unlike API keys for Firebase services, FCM server keys (used by the legacy FCM HTTP API) are sensitive and must be kept secret.
Keep service account keys secret
Also unlike API keys for Firebase services, service account private keys (used by the Firebase Admin SDK) are sensitive and must be kept secret.
Firebase Security Rules
Initialize rules in production or locked mode
When you set up Cloud Firestore, Realtime Database, and Cloud Storage, initialize your security rules to deny all access by default, and add rules that grant access to specific resources as you develop your app.
Use one of the default settings for new instances of Cloud Firestore (production mode) and Realtime Database (locked mode). For Cloud Storage, start with a security rules configuration like the following:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if false;
}
}
}
Security rules are a schema; add rules when you add documents
Don't write security rules after you write your app, as a kind of pre-launch task. Instead, write security rules as you write your app, treating them like a database schema: whenever you need to use a new document type or path structure, write its security rule first.
Unit test security rules with the Local Emulator Suite; add it to CI
To make sure your security rules are keeping up with your app's development, unit test your rules with the Firebase Local Emulator Suite and add these tests to your CI pipeline. See these guides for Cloud Firestore and Realtime Database.
Authentication
Custom authentication: mint JWTs from a trusted (server-side) environment
If you already have a secure sign-in system, whether a custom system or a third-party service, you can use your existing system to authenticate with Firebase services. Create custom JWTs from a trusted environment, then pass the tokens to your client, which uses the token to authenticate (iOS+, Android, Web, Unity, C++).
For an example of using custom authentication with a third-party provider, see the blog post, Authenticate with Firebase using Okta.
Managed authentication: OAuth 2.0 providers are the most secure
If you use Firebase's managed authentication features, the OAuth 2.0 / OpenID Connect provider options (Google, Facebook, etc.) are the most secure. You should support one or more of these providers if you can (depending on your user base).
Email-password authentication: set tight quota for the sign-in endpoint to prevent brute force attacks
If you use Firebase's managed email-password authentication service, tighten the
default quota of the identitytoolkit.googleapis.com
endpoints to prevent brute
force attacks. You can do so from the
Identity Toolkit API page
in the Google Cloud console.
Email-password authentication: Enable email enumeration protection
If you use Firebase's managed email-password authentication service, enable email enumeration protection, which prevents malicious actors from abusing your project's auth endpoints to guess account names.
Upgrade to Google Cloud Identity Platform for multi-factor authentication
For extra security on sign-in, you can add multi-factor authentication support by upgrading to Google Cloud Identity Platform. Your existing Firebase Authentication code will continue to work after you upgrade.
Anonymous authentication
Only use anonymous authentication for warm onboarding
Only use anonymous authentication to save basic state for users before they actually sign in. Anonymous authentication is not a replacement for user sign-in.
Convert users to another sign-in method if they'll want their data on other devices
Anonymous authentication data will not persist if the user clears local storage or switches devices. If you need to persist data beyond app restarts on a single device, convert the user to a permanent account.
Use security rules that require users to have converted to a sign in provider or have verified their email
Anyone could create an anonymous account in your project. With that in mind, protect all non-public data with security rules that require specific sign-in methods or verified email addresses.
For example:
allow write: if request.auth.token.firebase.sign_in_provider != "anonymous";
allow write: if request.auth.token.email_verified = true;
Cloud Functions safety
Never put sensitive information in environment variables
Often in a self-hosted Node.js app, you use environment variables to contain sensitive information like private keys. Do not do this in Cloud Functions. Because Cloud Functions reuses environments between function invocations, sensitive information shouldn't be stored in the environment.
To store Firebase API keys (which are not secret), just embed them in code.
If you're using the Firebase Admin SDK in a Cloud Functions, you don't need to explicitly provide service account credentials, because the Admin SDK can automatically acquire them during initialization.
If you're calling Google and Google Cloud APIs that require service account credentials, the Google Auth library for Node.js can get these credentials from the application default credentials, which are automatically populated in Cloud Functions.
To make private keys and credentials for non-Google services available to your Cloud Functions, use Secret Manager.
Encrypt sensitive information
If you can't avoid passing sensitive information to your functions, you must come up with your own custom solution to encrypt the information.
Simple functions are safer; if you need complexity, consider Cloud Run
Try to keep your functions as basic and understandable as possible. Complexity in your functions can often lead to hard-to-spot bugs or unexpected behavior.
If you do need complex logic or environment configurations, consider using Cloud Run instead of Cloud Functions.
Environment management
Set up development and staging projects
Set up separate Firebase projects for development, staging, and production. Don't merge client code to production until it's been tested against the staging project.
Limit team access to production data
If you work with a larger team, you can mitigate the consequences of mistakes and breaches by limiting access to production data using either predefined IAM roles or custom IAM roles.
If your team uses the Firebase Local Emulator Suite (recommended) for development, you might not need to grant wider access to the production project.
Library management
Watch out for library misspellings or new maintainers
When adding libraries to your project, pay close attention to the name of the library and its maintainers. A similarly-named library to the one you intend to install could contain malicious code.
Don't update libraries without understanding the changes
Look over the change logs of any libraries you use before you upgrade. Be sure the upgrade adds value, and check that the maintainer is still a party you trust.
Install watchdog libraries as dev or test dependencies
Use a library such as Snyk to scan your project for insecure dependencies.
Set up monitoring for Cloud Functions; check it after library updates
If you use the Cloud Functions logger SDK, then you can monitor and be alerted of unusual behavior, including behavior caused by library updates.