FIREBASE SECURITY CHECKLIST

Firebase exposes Firestore, Realtime Database, and Storage directly to the browser. The single most common Firebase breach pattern is allow read, write: if true left in production rules. The next most common is if request.auth != null — which sounds safe but means “any signed-up user can do anything to anyone’s data.” The checklist below is what we look for first when we audit a Firebase project.

Treat Critical as launch-blocking. High is week-one. Medium is the cleanup once you have users.

How to use this checklist

Walk it once in the Firebase console for your production project, ticking items as you go. Use the Rules Playground to actually test the rules against expected attacks (signed-out user reading a private doc; user A reading user B’s data). After the whole list passes, run a black-box scan against the deployed app to confirm the rules behave the way the playground says they do.

Critical (fix before launch)

1. Replace default-allow Firestore rules

Why it matters. New Firestore databases ship with rules that allow read/write for the first 30 days, then auto-deny. Many projects either miss the 30-day mark or “fix” the breakage by changing back to allow read, write: if true. This is the most common Firebase breach in the news.

How to check. In Firestore → Rules, read the rule file. Search for if true, if request.auth != null (without further scoping), and if true; patterns. Any of these on a non-public collection is a bug.

How to fix. Replace with collection-specific rules. For per-user data: allow read, write: if request.auth.uid == resource.data.userId. For role-gated: allow read: if request.auth.token.role == 'admin'. Test in the Rules Playground.

2. Lock Storage rules

Why it matters. Same problem as Firestore but for files. Default Storage rules either allow everything (in older projects) or auth-only (in newer ones), and “auth-only” still grants every signed-in user access to every file.

How to check. In Storage → Rules, read the rule file. Confirm rules scope by user and path: match /users/{userId}/{allPaths=**} { allow read, write: if request.auth.uid == userId }.

How to fix. Per-user paths with per-user scoping. For shared content, an explicit metadata field that the rule checks (request.auth.uid in resource.metadata.allowedUsers).

3. Restrict API key usage by domain and app

Why it matters. Firebase config (apiKey, authDomain, projectId) is by design embedded in the client. The apiKey is meant to identify the project, not to authenticate — but if it’s not restricted, attackers can use it from any domain to abuse free-tier services that bill against your project.

How to check. In Google Cloud Console → APIs & Services → Credentials, every API key for the Firebase project should have an HTTP referrer restriction (web) or app fingerprint (Android/iOS).

How to fix. Restrict each key by referrer to your domains only. For mobile apps, add SHA-1 / SHA-256 fingerprint restrictions.

4. Enable App Check on every Firebase service

Why it matters. App Check is what stops someone from running curl https://firestore.googleapis.com/... with your config and bypassing client-only rate limits. Without App Check, your Firebase quota and any per-request costs are at the mercy of anyone who reads your bundle.

How to check. In App Check, every service you use (Firestore, Storage, Functions, Realtime Database) should show “Enforced” — not “Monitor” mode.

How to fix. Configure App Check with reCAPTCHA Enterprise (web) or Play Integrity / DeviceCheck (mobile). Enable enforcement after a Monitor-mode period to confirm legitimate clients pass.

5. Audit Cloud Functions for missing auth context checks

Why it matters. Callable functions get context.auth for free if the caller is signed in — but the function has to actually check it. We routinely audit functions that read data.userId from the request and trust it, ignoring context.auth.uid entirely.

How to check. Open every callable function (HTTPS or onCall). For each, confirm the first lines verify context.auth exists and reject if not, and that any user ID used in queries comes from context.auth.uid, never from data.userId.

How to fix. Use the pattern: if (!context.auth) throw new HttpsError('unauthenticated', '...'). Then derive user ID from context.auth.uid. Validate request body shape with a schema before any database write.

6. Confirm rules deny by default

Why it matters. A rules file that doesn’t match a particular path leaves the path accessible according to the parent rule — which may be allow read, write: if true in a wildcard fallback. Rules failures are usually “I forgot to add the rule” not “the rule is wrong.”

How to check. In Rules Playground, simulate a request to a path you didn’t explicitly rule on. Confirm it denies, not allows.

How to fix. End every rules file with an explicit match /{document=**} { allow read, write: if false; } to deny anything that wasn’t matched above.

High (fix in the first week)

7. Enable email enumeration protection

In Authentication → Settings → User Actions, enable “Email enumeration protection”. Without this, signup, password reset, and magic link flows reveal whether an email has an account.

8. Configure auth provider OAuth redirect URIs

For each OAuth provider (Google, GitHub, etc.), restrict authorized redirect URIs to your real production domain only. Wildcards or localhost left from development are exploitable.

9. Add reCAPTCHA on signup and password reset

In Authentication → Settings → reCAPTCHA, enable reCAPTCHA Enterprise on signup, password reset, and email change flows.

10. Set Firestore document size and write rate limits

Firestore documents can grow up to 1 MB. Without rate limits in your Cloud Functions or rules, a single user can write thousands of large documents per second and exhaust your quota. Add per-user write throttles via Cloud Functions.

11. Verify firestore.indexes.json doesn’t leak schema

Composite indexes ship with the project. They can hint at the schema (which fields exist, what queries are run). Audit and remove indexes for queries you no longer make.

12. Enable budget alerts

Rule misconfig can cost thousands of dollars overnight (an open allow read: if true on a heavily-trafficked collection). Set Google Cloud budget alerts so you’re notified before the bill arrives.

Medium (fix when you can)

13. Disable unused auth providers

Every enabled auth provider is attack surface. Disable any provider your app doesn’t actually use.

14. Set up Security Rules unit tests

firebase emulators:exec plus @firebase/rules-unit-testing lets you run policy tests in CI. Cover the cross-user negative path: “user B cannot read user A’s document.”

15. Pin Cloud Functions runtime version

Pin to a specific Node.js version, not nodejs18 (which auto-updates within minor versions). Test before bumping.

16. Audit Firebase Hosting headers

In firebase.json, configure CSP, HSTS, and X-Frame-Options. Hosting doesn’t add them by default.

17. Restrict who has Owner / Editor on the Firebase project

Firebase project IAM mirrors GCP IAM. Audit who has Owner or Editor; downgrade where you can. Service accounts with broad roles are a common foothold.

18. Enable Firestore Point-in-Time Recovery

Available on the production tier. Enables restoring a corrupted database to a recent state without restoring from backups.

After every rules change

  • Run the Rules Playground for the cross-user negative path on every changed collection.
  • Run firebase emulators:exec with your rules unit tests.
  • Confirm the rules file still ends with the deny-by-default catch-all.
  • Verify App Check is still enforced on the changed service.
  • Re-test from a signed-out browser tab.

Common attack patterns we see in Firebase apps

The if true rule. Developer added a new collection, copied the default rule, never tightened it. Anyone can db.collection('payments').get() from the browser console.

The “auth.uid != null” mistake. Rules require auth but don’t scope by user. Signed-up attacker reads every document in the collection.

The unrestricted apiKey. Firebase config copied to a side project; apiKey not domain-restricted; attacker uses it from their own site to abuse the project’s quota.

The callable function trusting data.userId. Function reads user ID from request body, not from context.auth.uid. Attacker passes another user’s ID and gets back their data.

How to Secure Firebase

Step-by-step guide for hardening a Firebase project — rules patterns, App Check setup, Cloud Functions hardening, and the auth configuration above in long form.

Is Firebase Safe?

In-depth analysis of Firebase’s defaults — what the auto-deny does, where the rules engine bites, and what we find when we audit a typical Firebase app.

Automate Your Checklist

A checklist tells you what to look for. A scanner tells you what’s actually broken in the deployed app right now. VibeEval drives a real browser through your Firebase-backed app, attempts the cross-user rules bypasses and Storage enumeration above, and reports what got through — with the exact rule or function to fix.

SCAN YOUR FIREBASE APP

14-day trial. No card. Results in under 60 seconds.

START FREE SCAN