Firebase Security Rules: stop leaving your database open
Firebase web apps read and write Firestore, Realtime Database and Cloud Storage directly from the browser, using a config object that anyone can see. Security Rules are the only thing that decides whether a request is allowed. Rules left in test mode, or loosened to allow everything, expose your entire database to the internet.
What it is
Security Rules are a small policy language evaluated on Google's servers for every read and write. Each match block targets a path — a collection, a document, a storage folder — and allow statements say which operations are permitted under which conditions, using request.auth (who is asking) and request.resource.data (what they are trying to write).
The Firebase web config, including its apiKey, only identifies your project; it is not a secret and doesn't grant access on its own. That's why rules matter so much: any visitor can initialize the Firebase SDK with your config and query your database directly from their own browser console.
Why it matters
Firestore's test mode allows all reads and writes until an expiry date. When the deadline hits and the app breaks, a common "fix" is allow read, write: if true — which leaves every document world-readable and writable. Exposed Firebase databases are a recurring source of large data leaks, and they are trivial to find and dump.
Rules that only check request.auth != null aren't much better: anyone can create an account, often anonymously, and then read every user's data. Without validating writes, users can also edit fields they should never control — for example setting role: "admin" on their own profile.
How to fix it
Deny by default and grant access per collection. Here each user can only read and write their own document in users, and nothing else is reachable.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null
&& request.auth.uid == userId;
}
// Every other path is denied by default.
}
}Restrict which fields a user may change so they can't promote themselves or tamper with server-managed data. affectedKeys().hasOnly() rejects any update that touches other fields.
match /users/{userId} {
allow update: if request.auth.uid == userId
&& request.resource.data.diff(resource.data)
.affectedKeys().hasOnly(["displayName", "photoURL"]);
}Search your rules for match /{document=**} combined with a broad allow, for if true, and for expiring test-mode timestamps. Remember that Cloud Storage and Realtime Database have separate rules files — lock those down too.
Use the Rules Playground in the Firebase console or the Emulator Suite with @firebase/rules-unit-testing to run reads as an unauthenticated user and as a different signed-in user. A quick external check is an unauthenticated REST request — if it returns documents, the collection is public.
curl "https://firestore.googleapis.com/v1/projects/<project-id>/databases/(default)/documents/users"FAQ
Is my Firebase apiKey a secret?
No. The apiKey in the Firebase web config identifies your project and is meant to ship to the browser. Protect your data with Security Rules, and restrict the key in Google Cloud to the APIs your app actually uses so it can't be reused for other billable Google services.
Is request.auth != null enough?
Rarely. It only proves the requester is signed in — not that the data belongs to them. Compare request.auth.uid to the document owner, or check a role that users can't set themselves.
Does AppSafe check my Firebase rules?
AppSafe doesn't query your Firebase project, so it can't evaluate your rules — use the Rules Playground or the curl check above. It does flag Google API keys (the AIza… format) found in your JavaScript, because the same format is used for billable services like Maps or Gemini. For a Firebase web config that's expected; just make sure the key is restricted.
Is your app affected?
AppSafe checks for this and dozens of other issues in one free scan.
Scan my app freeRelated guides
Supabase Row Level Security (RLS): lock down your tables
Your anon key is public. RLS is the only thing between it and your data.
API keys exposed in client-side JavaScript
Anything in your bundle is public. Treat it that way.
Exposed database backups: what happens when /backup.sql is reachable
A reachable .sql dump is a complete hand-off of your entire database.