Blog · 2026-08-11
Firebase security rules — the two-minute test that shows whether your Firestore is public
By the howsafeismyapp team
Supabase gets most of the attention in AI-builder discussions, but plenty of Bolt and v0 apps ship with Firebase — and Firestore has its own version of the open-database problem: test mode.
When you create a Firestore database, the console offers "test mode" so development just works. Test mode means: anyone with your project config (which ships in your JavaScript bundle, by design) can read and write everything, usually with an expiry date the generated app never revisits. The result is the same finding we flag on Supabase apps: a database readable without a login. (Practical guidance, not legal advice.)
Why your Firebase config being public is fine — and rules being open is not
Like Supabase's anon key, the Firebase config object (apiKey, projectId, …) is meant to be public. It identifies your project; it doesn't authorize anything. Authorization is entirely the job of security rules. If your rules say allow read, write: if true — or test mode's date-based equivalent — the "api key is public" design becomes "the database is public."
The two-minute test
- Open your deployed app in a private window, DevTools → Network, and note the
projectIdfrom requests tofirestore.googleapis.com. - Logged out of everything, request a collection you know exists via the REST endpoint:
https://firestore.googleapis.com/v1/projects/PROJECT_ID/databases/(default)/documents/users - Documents back = your Firestore answers to strangers. If those documents contain names, emails, or user content, you're looking at an Art. 32 GDPR problem — and if real personal data was exposed, at the breach-notification questions of Art. 33.
Locking it down
In the Firebase console under Firestore → Rules:
- Default deny. Start from
allow read, write: if false;for everything, then open specific paths deliberately. - Per-user data:
allow read, write: if request.auth != null && request.auth.uid == userId;on/users/{userId}— the Firestore equivalent of a Supabase RLS policy. - Public content (a feed, a catalog):
allow read: if true; allow write: if false;— public read can be a product decision; public *write* almost never is. - Re-run the two-minute test. An error or empty response while logged out is success. The inside view (rules editor) and the outside view (what a stranger can fetch) can disagree — the outside view is the one that counts.
If your app uses the Realtime Database instead of Firestore, the same logic applies to its JSON rules — and its test mode is even more permissive.
Don't stop at the database
Firebase apps from AI builders share the rest of the checklist with everyone else: tracking that fires before consent, missing security headers (test yours in seconds: security headers check), and the legal pages nobody generates. The full pass for your builder: Bolt checklist · v0 checklist.
Common questions
Is my Firebase API key a secret?
No — it ships in your bundle by design and identifying it is not a vulnerability. The things that must stay secret are service-account JSON files and admin SDK credentials; those belong on servers only, never in frontend code: admin key exposed in frontend code.
Firestore warned me about test mode expiring — what happens then?
When the expiry date passes, all client access gets denied and your app simply breaks for users. That's Firebase protecting you from permanent test mode — but the right response is writing real rules, not extending the date another 30 days.
Is Firebase itself GDPR compliant?
It can be operated compliantly: Google offers a DPA, and you should address the US-transfer question in your privacy policy and configure data locations where possible. The practical failures we see aren't Google's paperwork — they're open rules and missing disclosure in the operator's privacy policy.
Check everything at once
Our free passive scan tests your app the way any anonymous browser would — including whether your backend answers without a login — plus 14 other checks. One minute, no signup, nothing stored.