Your Firebase config is public. Your rules are not optional.
Every Firebase web app ships a config object: apiKey, authDomain, projectId, storageBucket and so on. People often panic when they see it in the page source, but Firebase's own documentation is clear that this config identifies your project; it isn't a password. The apiKey starting with AIza lets the client reach Firebase services, and it can't grant data access on its own.
What protects your data is Firebase Security Rules for Firestore, Realtime Database and Cloud Storage. With your config, anyone can call those services directly from a script, skipping your UI entirely. If your rules allow it, they get the data.
The test-mode trap
When you create a Firestore database in test mode, Firebase writes rules like allow read, write: if request.time < timestamp.date(…); which open everything to everyone for 30 days. Realtime Database test mode is ".read": true, ".write": true. Tutorials and AI editors often start this way to "make it work", and the rules quietly ship to production. Another common pattern is allow read, write: if request.auth != null; which looks safe but lets any signed-in user, including one who just created a free account, read and overwrite every other user's documents.
What this checker does
- Finds your Firebase config (projectId, databaseURL, apiKey) in your HTML and same-site JavaScript bundles.
- Reports Google API keys as informational, with a reminder to restrict them.
- Flags real secrets in the same pass, such as a service-account private key accidentally bundled from an admin SDK setup.
- Tests data exposure for verified owners. Once you verify you own the site, ShipSniffer checks whether the Realtime Database root and common Firestore collections (users, posts, messages, orders, profiles, items) are readable without authentication. We don't probe other people's databases.
Rules that actually protect users
Start from deny-all and open only what each collection needs:
rules_version = '2';
service cloud.firestore {
match /databases/{db}/documents {
match /users/{uid} {
allow read, write: if request.auth != null && request.auth.uid == uid;
}
match /posts/{id} {
allow read: if resource.data.published == 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;
}
}
}
For Realtime Database, set ".read": false and ".write": false at the root and grant per path, for example "users": { "$uid": { ".read": "auth.uid === $uid", ".write": "auth.uid === $uid" } }. For Storage, scope uploads to request.auth.uid folders and check content type and size.
Lock down the API key too
In Google Cloud Console → APIs & Services → Credentials, add HTTP referrer restrictions for your domains and limit the key to the Firebase APIs you use. Turn on App Check to make it harder for scripts outside your app to call your backend. Neither replaces rules, but both cut abuse such as quota theft.
Test like an attacker
Use the Rules Playground in the Firebase console to simulate an unauthenticated read of /users/someone, and an authenticated read of another user's document. Both should be denied. Then run the full ShipSniffer scan for headers, keys and launch polish.