Firebase Security Rules: 8 Common Mistakes That Expose Your Data
Firebase puts your database one rule away from the public internet. Here are the eight Security Rules mistakes that leak user data — and the correct patterns for each.
Your rules ARE your backend
With Firebase, the client talks to the database directly. There's no server in the middle to enforce access — your Security Rules are the entire authorization layer. Get them wrong and your data is exposed to anyone with your (public) project config.
Here are the eight mistakes we find most often.
1. Shipping test mode to production
New projects offer "test mode," which is this:
// Firestore — test mode. ANYONE can read/write everything.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.time < timestamp.date(2026, 4, 1);
}
}
}
That date isn't security — it's a countdown to a wide-open database. Replace it before you have a single real user.
2. allow read, write: if true
The other classic. It looks intentional, so it survives review. It means the public can read and write your entire database. Never deploy it.
3. Authenticated does not mean authorized
This rule lets any signed-in user read every other user's document:
// WEAK — any logged-in user sees everyone's data
match /users/{userId} {
allow read: if request.auth != null;
}
Scope reads to the owner:
// CORRECT — you can only read your own document
match /users/{userId} {
allow read, write: if request.auth != null
&& request.auth.uid == userId;
}
4. Not validating written data
Without validation, a user can write any shape, including fields like role: "admin" or isPaid: true:
match /orders/{orderId} {
allow create: if request.auth != null
&& request.resource.data.userId == request.auth.uid
&& request.resource.data.amount is number
&& request.resource.data.amount > 0
&& !("status" in request.resource.data); // server sets this, not the client
}
Validate types, ownership, and which fields the client is even allowed to set.
5. Trusting client-set privilege fields
Never store isAdmin/role in a document the user can write. Use custom claims on the auth token instead:
// Set via Admin SDK server-side: setCustomUserClaims(uid, { admin: true })
match /adminPanel/{doc} {
allow read, write: if request.auth.token.admin == true;
}
Claims live in the signed token and can't be tampered with by the client.
6. Forgetting Storage rules entirely
Firestore and Storage have separate rule files. Locking down Firestore while leaving Storage open is extremely common. Secure both:
service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/{fileName} {
allow read, write: if request.auth != null
&& request.auth.uid == userId
&& request.resource.size < 5 * 1024 * 1024
&& request.resource.contentType.matches('image/.*');
}
}
}
7. The recursive wildcard catch-all
match /{document=**} matches every path at every depth. A permissive rule there silently overrides your careful per-collection rules. Audit any =** match and make sure it isn't granting access you forgot about.
8. No rules testing in CI
Rules are code. Test them like code with the emulator:
const { assertFails, assertSucceeds } = require("@firebase/rules-unit-testing");
it("blocks reading another user's doc", async () => {
const alice = testEnv.authenticatedContext("alice");
await assertFails(alice.firestore().doc("users/bob").get());
});
Run it in CI so a bad deploy can't ship.
How to verify quickly
- Open the Firebase console → Rules → Rules Playground. Simulate an unauthenticated read of a user doc. It must deny.
- Grep your rules for
if true,=**, andrequest.auth != nullwithout auidcomparison. - Confirm Storage rules exist and aren't open.
Checklist
- No test mode /
if truein production - Reads and writes scoped to the owner's
uid - Written data validated (types, fields, ownership)
- Privilege via custom claims, not client fields
- Storage rules locked down and size/type limited
- Recursive
=**matches audited - Rules tested in CI
Scan it with Troja
Troja probes your Firebase project the way an attacker would — testing whether your Firestore and Storage rules actually deny unauthorized reads — and flags exposed collections before they end up in a breach disclosure. Run a scan and find out what's reachable.
Run the scan this post is about.
Free, no signup. See what's hiding inside your walls in ~30 seconds.
Keep reading
All postsTroja vs. checkvibe: the closest scanner comparison (2026)
checkvibe pioneered security + SEO + AEO scanning with AI fix prompts and a 7-engine matrix. Troja matches it and adds connected deep-stack scans. The honest comparison.
ReadTroja vs. Fixnx: which AI website scanner should you use?
Fixnx runs 100+ AI-powered security, SEO and speed checks with credit-pack pricing. Troja adds AEO, connected deep-stack scans and per-finding AI fixes. Compared.
ReadTroja vs. CyScan.io: recon tool vs. fix-it scanner
CyScan.io is a free attack-surface recon scanner — endpoints, subdomains, fuzzing, screenshots. Troja is a fix-and-ship scanner with AI fixes, AEO and deep-stack scans.
Read