Firebase Security Rules Audit: The Misconfigurations That Expose Your App
Someone spun up your Firestore in test mode two years ago to ship a demo. The console handed them a rule that expired after 30 days, the app broke on that date, and to unbreak it someone pasted in allow read, write: if true. That line is still in production. Every document in your database is world-readable and world-writable to anyone who knows the project ID — and the project ID ships in your JavaScript bundle.
This is the most common finding in a Firebase security rules audit, and it is almost never caught by monitoring, because nothing errors. The app works. Data flows. The rules are simply doing nothing. Firebase's default posture pushes security entirely into declarative rules files that few teams review after launch, so misconfigurations sit silently until someone scrapes the database or runs up your bill.
This guide walks the misconfigurations that actually matter — what each one is, why it happens, one console path or command to detect it, and the sober real-world consequence. Run the checks today; they take about an hour across a typical project.
This is part one of a three-part series. Part two is a DIY Firebase security audit using only native tools; part three covers continuous auditing with an AI agent.
1. Test-mode rules that never got locked down
What it is. When you create Firestore, Storage, or the Realtime Database in "test mode," the console generates a time-boxed open rule — for Firestore it looks like allow read, write: if request.time < timestamp.date(2026, 8, 12);. It grants unrestricted access until that date, then denies everything. The trap is what happens on expiry: the app breaks, and the fastest fix an engineer under pressure reaches for is allow read, write: if true;, which never expires and never says no.
Why it happens. Test mode is the path of least resistance during prototyping, and "we'll lock it down before launch" is a task with no owner. The expiry breakage arrives long after the person who created it has moved on.
How to detect it. Open Firebase Console → Firestore Database → Rules. Read the rules top to bottom. Any allow read, write: if true, any if request.time < timestamp.date(...) with a date in the future, or a catch-all match /{document=**} granting broad access is a finding. Repeat for Storage → Rules and Realtime Database → Rules.
Typical consequence. Full read means anyone can scrape every record — user PII, private messages, internal config. Full write means anyone can overwrite or wipe your collections. This is the pattern behind most public "misconfigured Firebase database" disclosures.
2. Firestore rules that check auth but not ownership
What it is. The second-most-common Firestore security rules mistake looks responsible at a glance: allow read, write: if request.auth != null;. It requires a signed-in user — but it does not check which user. Any authenticated account, including one an attacker creates themselves in seconds through your own public sign-up, can read and write every other user's documents.
Why it happens. "Require login" feels like the security control. The jump from "is authenticated" to "owns this specific document" requires per-collection ownership logic that is easy to defer.
How to detect it. In Firestore Database → Rules, find every rule guarded only by request.auth != null with no comparison against a document field or path segment. A correct per-document rule ties identity to the resource, for example:
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
If your rules never reference request.auth.uid alongside userId or resource.data.ownerId, ownership is not enforced.
Typical consequence. Horizontal privilege escalation: user A reads and edits user B's data. Because sign-up is usually open, "authenticated" is not a meaningful barrier — the attacker simply registers.
3. Cloud Storage rules left wide open
What it is. Storage rules are a separate ruleset from Firestore, and they are frequently forgotten. A bucket with allow read, write: if true; — or the read-open, write-authenticated variant — lets anyone download every uploaded file and, worse, upload arbitrary content that counts against your storage and egress bill.
Why it happens. Teams lock down Firestore and assume Storage inherited the same posture. It did not; each product surface has its own rules file.
How to detect it. Open Firebase Console → Storage → Rules. Look for allow read or allow write without an auth or ownership condition, and confirm write paths are scoped to a per-user prefix. A safe pattern scopes uploads to the owner:
match /user-uploads/{userId}/{fileName} {
allow write: if request.auth != null && request.auth.uid == userId;
}
Typical consequence. Public read exposes user-uploaded documents, avatars, receipts, and ID scans. Public write turns your bucket into free hosting for whatever strangers upload — malware, illegal content, and a bandwidth bill you did not budget for.
4. Realtime Database rules anti-patterns
What it is. The Realtime Database uses a different, JSON-based rules language — .read and .write expressions — and its own anti-patterns. The classic is a top-level ".read": true or ".write": true, which cascades to every child node. A subtler one: rules that grant access at a high path (/users) instead of scoping to /users/$uid, so any user sees the whole tree.
Why it happens. RTDB rules cascade downward — granting access to a parent grants it to all children — and that inheritance is easy to get wrong. Legacy projects that predate Firestore often still run RTDB with rules nobody has revisited.
How to detect it. Open Firebase Console → Realtime Database → Rules. A correctly scoped rule looks like:
{
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
Any ".read": true or ".write": true, or an auth != null check that is not paired with a $uid match, is a finding.
Typical consequence. A single open .read at the root exposes the entire database in one request to https://your-db.firebaseio.com/.json. Open .write allows wholesale data destruction.
5. Missing App Check
What it is. Security rules decide what a request may do; they cannot verify the request came from your actual app. App Check closes that gap by attesting that traffic originates from your genuine web or mobile client (via reCAPTCHA, DeviceCheck, or Play Integrity). Without it, anyone can hit your Firebase backends directly with a script using nothing but the public config, and your rules are the only thing standing between them and your data.
Why it happens. App Check is opt-in and off by default. Teams reasonably fear enforcing it will break existing clients, so it stays in the "monitor" phase — or never gets enabled at all.
How to detect it. Open Firebase Console → App Check. Check each product (Firestore, Storage, Realtime Database, Authentication) for its enforcement state. "Unenforced" or "Monitoring" means App Check is not actually blocking anything.
Typical consequence. Automated abuse: scripted scraping of data that rules permit to authenticated users, credential-stuffing against your Auth endpoints, and quota or billing abuse that rules alone cannot stop.
6. Unenforced Firebase Authentication settings
What it is. Firebase Authentication ships with protections that are not all on by default. Email enumeration protection, which stops attackers from probing which email addresses have accounts, is one. Unlimited public sign-ups are another — if your Firestore rules trust "any authenticated user," an open sign-up flow is a self-service key.
Why it happens. Auth is configured once during setup and rarely revisited. The defaults optimize for a frictionless first launch, not a hardened production posture.
How to detect it. Open Firebase Console → Authentication → Settings and review user-actions and abuse-prevention options. Under Sign-in method, confirm only the providers you actually use are enabled, and check Authentication → Settings → Authorized domains — stale entries (a preview domain, a contractor's tunnel) let auth flows run from hosts you no longer control.
Typical consequence. Account enumeration feeds targeted phishing. Unbounded sign-ups combined with weak rules give attackers the "authenticated" status your rules trust. Stale authorized domains enable OAuth redirect abuse.
7. The exposed web API key that isn't actually a secret
What it is. Firebase web API keys appear in every client bundle, and security scanners flag them constantly. Here is the sober reality: a Firebase web API key is not a secret. It identifies your project to Google's servers; it does not grant access to data. Access is governed entirely by Security Rules and App Check. A leaked web API key, on its own, does not expose your database.
Why it matters anyway. The key is not harmless. It can be used to call Identity Toolkit endpoints — for example, to create accounts against your project — which is why unrestricted sign-up plus a public key enables the abuse in sections 2 and 6. And a leaked service account key (a JSON file for the Admin SDK) is an entirely different, genuinely critical exposure that bypasses all rules.
How to detect it. In the Google Cloud Console → APIs & Services → Credentials, review each API key's application restrictions (HTTP referrers for web) and API restrictions. Confirm no service-account JSON files live in your repo — git log -p | grep -i "private_key" is a fast first pass.
Typical consequence. Chasing the web API key wastes an audit; missing a committed service-account key is a full compromise. Spend your attention accordingly.
Why a one-time audit isn't enough
Every fix above holds only until the next deploy. Rules are code, and code regresses: a teammate widens a match block to unblock a feature, a new collection ships without an ownership check, someone toggles App Check back to monitoring to debug a client and forgets to re-enforce it. None of these throw an error. The app keeps working, and the exposure lands silently between audits.
The fix is to make the rules review continuous rather than annual. Start manual — the next article walks through a full DIY audit using the Firebase CLI, the Rules Playground, and the Emulator Suite — but know that a point-in-time pass ages the moment the next commit merges.
Audit your rules continuously
Everything above can run on autopilot. Olivier, CloudThinker's security agent, connects to your Firebase project read-only, inventories your apps, and audits Firestore, Storage, and Realtime Database rules continuously — flagging open rules, missing ownership checks, and App Check gaps the day they appear, and alerting you when a rules release widens access. Nothing changes without your approval.
Try CloudThinker free — 100 premium credits, no card required — or keep going with the DIY audit guide.
