I picked 35 random Firebase project IDs from public GitHub repos this morning and probed each for publicly readable Firestore collections. No auth, no special tools — just plain GET requests to firestore.googleapis.com.
8 of them — 23% — returned data to an anonymous request.
Here's the raw breakdown:
| Collection | # of projects leaking |
|---|---|
users |
4 |
products |
3 |
posts |
2 |
messages |
1 |
profiles |
1 |
orders |
1 |
12 leaks across 8 projects in a 35-sample slice.
How is this possible?
Firebase apps bundle a firebase-config.js into every web build. That config contains the project ID. The project ID is not secret — it's in the URL, in the JS bundle, in any .env.example somewhere. The actual security boundary is your firestore.rules file.
If your rules look like this:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
…anyone in the world with your project ID can read every document in every collection. Test-mode rules ship with this exact pattern; they're meant to be temporary.
The classic mistakes I see again and again
After running this scan + reading hundreds of firestore.rules files in the wild:
1. Test-mode never replaced
match /{document=**} {
allow read, write: if request.time < timestamp.date(2099, 1, 1);
}
Someone wanted "test mode but not 30 days," changed the date to 2099, forgot about it. Three years later, still wide open.
2. Auth-only without ownership
match /users/{uid} {
allow read, write: if request.auth != null;
}
This means any logged-in user can read or write any other user's document. Sign up with a throwaway account → read everyone's data.
3. Read-open, write-protected
match /products/{id} {
allow read: if true;
allow write: if request.auth.token.admin == true;
}
Innocent for product catalogs, fatal for /payments or /users.
4. Wildcard match with no terminator
match /tenants/{tid}/{document=**} {
allow read, write: if request.auth.token.tenant_id == tid;
}
Looks fine. But if the token is forged or tenant_id claim is missing, the rule grants access. Most apps don't validate the claim is present.
How to scan your own project
I built a free in-browser scanner at perufitlife.github.io/firebase-security-skill/scan.html — paste your project ID, watch it probe a list of common collection names. The scan runs in your browser; nothing is sent to my server.
If you'd rather scan from the CLI:
npx firebase-security ./firestore.rules --project-id YOUR_ID
…or from Apify (no install): apify.com/renzomacar/firebase-security-auditor
Source code is MIT: github.com/Perufitlife/firebase-security-skill
A note on disclosure
I'm not naming any of the 8 projects here. The aggregate stat is the point — the same scan against any random sample of public Firebase configs will produce a similar leak rate. If you'd like me to re-run with names privately so you can verify against your own (or check if you're in the original sample), email me: renzomacar@gmail.com.
What "fixing it" looks like
For most leaks, the change is one block. Replace if true with the appropriate auth + ownership check for your data model. Example for a /users/{uid} collection where each user owns their own doc:
match /users/{uid} {
allow read: if request.auth != null && request.auth.uid == uid;
allow write: if request.auth != null && request.auth.uid == uid;
}
The auditor I built generates one of these snippets per finding, paste-ready.
If you find leaks in your project and want a written audit report (PDF) with all the snippets bundled + 30 days of follow-up Q&A, I do a $29 lite tier (top 3 fixes) and a $99 full audit (every match block, 24h delivery): perufitlife.github.io/firebase-security-skill
But please run the free scan first. Most projects find their own leaks in the report.
Top comments (0)