Home › Blog › Firebase Security Rules
Firebase Security Rules: 7 mistakes that leak your whole database
October 12, 2026 · ~7 min read
Firebase Security Rules are the only thing standing between your database and the internet. When you ship a SaaS fast — especially with AI help — that is exactly the layer that tends to get left for later. Below are the 7 mistakes we see most in audits, each with the wrong code, the right code, and why it matters.
1. The test-mode rule that shipped to production
Every Firebase project starts with a temporary rule. It is mistake number one because it is literally the default, and nobody remembers to change it back.
// ❌ Anyone on the internet reads and writes everything match /{document=**} { allow read, write: if true; }
A subtler variant uses an expiry date that has already passed (request.time < timestamp.date(2026, 1, 1)) — after the deadline the rule denies everything, so someone "fixes" it by swapping in if true. The fix is to never leave if true on a collection with real data: every read and write needs an explicit condition.
2. Confusing "is logged in" with "is the owner"
This is the classic multi-tenant SaaS leak. The rule only checks whether an authenticated user exists — so any logged-in customer can read every other customer's data.
// ❌ Any logged-in user reads any order match /orders/{id} { allow read: if request.auth != null; } // ✅ Only the document owner reads match /orders/{id} { allow read: if request.auth != null && resource.data.ownerId == request.auth.uid; }
In OWASP terms this is an IDOR/BOLA (Broken Object Level Authorization) — the most common authorization flaw in APIs. Every collection with per-customer data must tie the document to request.auth.uid (or the user's tenant/clinicId). It's the subject of our guide on multi-tenant isolation.
3. Trusting a field the client controls
You can write a rule that looks safe but reads the user's role from a place the user can change:
// ❌ "role" comes from the document the user writes allow write: if request.resource.data.role == 'admin'; // ✅ "admin" comes from the auth token (custom claims), not client-editable allow write: if request.auth.token.admin == true;
Permissions must come from a source the client cannot forge: custom claims on the token, or a document only the back-end writes. Never from a field submitted in the very write the rule is meant to protect.
4. Not validating what is being written
Rules are not just "allow or deny" — they are your data-integrity validator at the database layer. Without validation, a client can write a negative balance, an "enterprise" plan they never paid for, or inflate a usage counter.
// ✅ Validate shape, size and immutable fields match /profiles/{uid} { allow update: if request.auth.uid == uid && request.resource.data.plan == resource.data.plan // can't change own plan && request.resource.data.email is string && request.resource.data.name.size() < 120; }
5. The recursive wildcard that opens everything below
{document=**} matches the collection and every subcollection. A permissive rule at the wrong level leaks everything nested inside it.
// ❌ Also exposes the /users/{uid}/private subcollection match /users/{uid}/{document=**} { allow read: if request.auth != null; }
Remember: rules do not cascade top-down (a rule on /users does not protect /users/x/cards), but =** does cascade downward. Be explicit at each level.
6. Securing Firestore and forgetting Storage
Firestore, Realtime Database and Cloud Storage have separate rule sets. It is common to lock down Firestore and leave Storage on the open default — and users' uploads (documents, receipts, photos) become public to anyone with the URL.
// storage.rules — ✅ each user only touches their own folder match /uploads/{uid}/{file} { allow read, write: if request.auth != null && request.auth.uid == uid; }
7. Assuming Rules replace the back-end
Security Rules protect database access, but they do not rate-limit, do not verify webhook signatures, do not stop a bot, and do not validate complex business logic. For the rest you still need App Check (proves the call comes from your app), Cloud Functions for sensitive operations, and server-side validation. Rules are one layer, not the whole wall.
get; and grep your rules files for any if true, a bare request.auth != null (with no ownership check), and {document=**}.
Don't want to hunt for these by hand?
SAAS SHIELD runs a security review of your React/Next.js repository on Firebase or Supabase and returns every flaw with its location, evidence and fix.
Request an audit →