Firebase Security Rules misconfigurations
Updated 2026-05-12What this is
Firebase Realtime Database, Firestore, and Cloud Storage all gate client access through a rules file. The rules file is the entire authorization layer. There is no per-route auth in front of it; the client SDK talks directly to the database, and the rules file decides what comes back.
The two failure shapes:
// firestore.rules
match /users/{userId} {
allow read: if true; // public read on every user document
allow write: if request.auth != null; // any logged-in user can write any user
}
// storage.rules
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null; // any logged-in user can touch any file
}
}
if true reads as "no condition, always allow." if request.auth != null reads as "any authenticated session is fine," which makes signup the only step between an attacker and your data.
Why it matters
The Firebase client SDK ships with the project's API key, which is intentionally public. Access control depends entirely on the rules file. A permissive rule turns the database into a REST API that anyone with a browser can query. The blast radius is every row matched by the rule's path glob.
A match /users/{userId} rule with allow read: if true exposes every user document. That typically includes email, profile data, and any sensitive fields the app stores there. A storage rule with allow read: if request.auth != null plus a free signup flow turns the storage bucket into a public file share for the world.
What the failure looks like
PreFlight scans firestore.rules, database.rules.json, and storage.rules files for:
allow read: if true(orif trueon any operation).allow read, write: if request.auth != nullon storage rules without further conditions.- Default-deny absent: rules files that omit a top-level deny block on paths that should be private.
What the fix looks like
Tie every rule to the principal:
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
}
match /posts/{postId} {
allow read: if resource.data.public == true || request.auth.uid == resource.data.authorId;
allow create: if request.auth.uid != null && request.resource.data.authorId == request.auth.uid;
allow update, delete: if request.auth.uid == resource.data.authorId;
}
For storage, scope by path and require ownership:
match /b/{bucket}/o {
match /users/{userId}/{filename} {
allow read, write: if request.auth.uid == userId;
}
}
The pattern: every rule references request.auth.uid and compares it to a property of the resource. Absence of that comparison is the bug.
Related
- Supabase RLS covers the equivalent class on Supabase / Postgres. The mechanisms differ; the access-control discipline is identical.
- Admin route exposure covers the matching pattern on application routes.
Sources
The Firebase security-rules docs are the authoritative reference. CWE-285 names the underlying vulnerability class. OWASP A01:2021 ranks broken access control as the highest-prevalence application risk; Firebase rules misconfigurations are one of its most reproducible expressions.
RELATED PROBES
- · Firebase Rules