Troja
All posts
FirebaseMar 22, 2026·15 min read

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.

By Security Desk

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

  1. Open the Firebase console → Rules → Rules Playground. Simulate an unauthenticated read of a user doc. It must deny.
  2. Grep your rules for if true, =**, and request.auth != null without a uid comparison.
  3. Confirm Storage rules exist and aren't open.

Checklist

  • No test mode / if true in 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.

Free scan · no signup · results in ~30 seconds
Firebase Security Rules: 8 Common Mistakes That Expose Your Data — Troja