← All posts
·7 min read·GuardLayer

Firestore test mode expired? Don't fix it with if true

FirebaseFirestoreSecurity RulesData Leak

TL;DR: Test-mode rules let anyone with your project's config read and overwrite your whole Firestore database until a date in the rule, then deny everything, which is when the app breaks. The two common quick fixes, allow read, write: if true; and pushing the date forward, put the database back in that wide-open state. The fix is rules that check who is asking, plus queries that filter on the same field the rule checks.

What is Firestore test mode?

When you create a database in the Firebase console you pick a starting mode. Production mode denies all reads and writes from web and mobile clients. Test mode does the opposite: Firebase's own quickstart says it "allows anyone to read and overwrite your data". The rule it writes looks like this, with a date roughly a month out:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.time < timestamp.date(2026, 12, 1);
    }
  }
}

{document=**} matches every document in the database, and the only condition is the clock. Until that date, there is no access control at all.

"Anyone" is literal. The web config you paste into your frontend (apiKey, projectId) is not a secret, and Firebase says so: those values identify your project, they don't protect it. Anyone who opens your site's JavaScript has them, and can call Firestore directly, outside your app and its UI.

What happens when test mode expires?

The condition turns false and every client read and write fails, usually with:

FirebaseError: Missing or insufficient permissions.

The app that worked yesterday now shows empty lists and failed saves. This is the moment the dangerous fixes happen, because the error looks like a broken setting rather than a security boundary doing its job.

Why are "if true" and a new date the wrong fix?

Both make the error go away by removing the boundary again:

// Quick fix 1: no expiry at all
allow read, write: if true;

// Quick fix 2: another month of test mode
allow read, write: if request.time < timestamp.date(2027, 6, 1);

Firebase's guide to insecure rules shows the first one under the label "Not recommended" and says to never use it in production. The second is the same rule with a later date. Either way, anyone can read every collection, change any document, and delete everything.

This is not a theoretical risk. In March 2024 researchers reported more than 900 websites with misconfigured Firebase backends, exposing about 125 million user records: names, email addresses, phone numbers, billing details and around 20 million passwords. When they notified the owners, only about a quarter fixed it.

How do I write real Firestore security rules?

Start from "deny everything", then open exactly what each collection needs, based on who is signed in. A typical shape for user data plus public posts:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Each user reads and writes only their own profile.
    match /users/{userId} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }

    // Posts are public to read; only the author can write.
    match /posts/{postId} {
      allow read: if true;
      allow create: if request.auth != null
                    && request.resource.data.authorId == request.auth.uid;
      allow update, delete: if request.auth != null
                    && resource.data.authorId == request.auth.uid;
    }

    // Anything not matched above stays denied.
  }
}

Three details carry the weight:

  • request.auth.uid == userId ties a document to its owner. Without it, any signed-in user can read every user's data.
  • request.resource.data is the document as it would be after the write, so a user can't create a post under someone else's id.
  • allow read: if true is fine where data is meant to be public. The problem is never true on its own; it's true on {document=**}, or on a collection that holds private data.

Is request.auth != null enough?

No. It means "anyone who is signed in", and with open sign-up that is anyone who creates an account. Firebase's insecure-rules guide calls this out: confirm that you want any logged-in user to have access. The same guide warns about checking an email domain (request.auth.token.email.matches('.*@yourcompany[.]com$')): emails aren't always verified at sign-in, so also require request.auth.token.email_verified.

I locked it down and now I get "Missing or insufficient permissions"

If your rules are right, the usual cause is the query. Firestore rules are not filters. Firebase's docs put it plainly: queries are "all or nothing". If a query could return a document the user isn't allowed to read, the whole query fails, even when every document it would actually return is theirs.

Public collections like posts don't hit this, because anyone may read them. A private collection does:

match /notes/{noteId} {
  allow read, write: if request.auth != null && request.auth.uid == resource.data.ownerId;
}

this query fails, because nothing limits it to the user's own notes:

const snap = await getDocs(collection(db, "notes"));

and this one works, because it filters on the same field the rule checks:

const q = query(collection(db, "notes"), where("ownerId", "==", auth.currentUser.uid));
const snap = await getDocs(q);

Don't "fix" this by loosening the rule. Make the query ask only for what the rule allows.

How do I test rules before deploying them?

  • Rules Playground (Firebase console, Rules tab): pick an operation, a path and a signed-in or anonymous user, and run it against your draft rules.
  • Firestore emulator with @firebase/rules-unit-testing: write tests that assert a signed-in user can read their own data and can't read someone else's. The second kind of test is the one that catches leaks.
  • Keep rules in your repo (firestore.rules, listed in firebase.json) and deploy them with firebase deploy --only firestore:rules, so every change is reviewed like code. Rules deployed from your project directory overwrite what's in the console.

Realtime Database has the same trap with different syntax. Its open rules are { "rules": { ".read": true, ".write": true } }, and the fix is the same idea: scope reads and writes to auth.uid.

FAQ

Is my Firebase API key a secret? No. The web config, including apiKey, ships in every client and identifies your project. Your security rules are what protect your data, which is why open rules are a leak even though nothing "leaked".

How do I know if my Firestore is open right now? Open the Rules tab in the Firebase console. If you see if true or if request.time < timestamp.date(...) on {document=**}, the whole database is open to anyone until that date. Firebase can also send you an alert email when it detects insecure rules.

Can I keep test mode during development? Use the Firestore emulator for development instead. Test mode on a real project is reachable by anyone who finds the config, and dev projects often end up holding real data.

Do Cloud Functions and the Admin SDK still work with strict rules? Yes. Server code using the Admin SDK bypasses security rules, so locking down client access doesn't break your backend. That also means server code must do its own authorization.

I'm moving from Firebase to Supabase. Is it the same problem there? The same shape, different names: a Supabase table without Row Level Security is readable by anyone with the publishable key. See what happens when Supabase RLS is disabled, and using Firebase Auth with Supabase RLS if you're keeping Firebase for sign-in.

Where GuardLayer fits (and where it doesn't)

Straight up: GuardLayer doesn't check Firebase today. It scans Next.js and Supabase code, and its Live check reads a connected Supabase project's real settings. It doesn't read firestore.rules or a Firebase project, so it won't flag the test-mode rule above.

If a check for firestore.rules (open {document=**} rules, expiring test mode, auth != null on private collections) would be useful to you, tell us. That's how we decide what to build next.

Sources: Firebase: Fix insecure rules, Firebase: Securely query data, Firebase: Get started with Security Rules, SecurityWeek: Misconfigured Firebase instances expose 125 million user records, The Register, March 2024.

Scan your Next.js + Supabase app — free

GuardLayer runs 36 checks — exposed keys, missing RLS, over-permissive policies, unprotected Server Actions — with the exact fix inline. Connect GitHub and it checks every push too — free on public repos.

No signup, no card. Raw files aren’t kept; only the report is saved.

Keep reading