VibeSecurity

Platform checklist

Firebase security checklist for AI-built apps

Firebase lets a web or mobile client talk straight to your data, so your security rules are the backend. AI tools often start projects in test mode or paste an allow-everything rule to get past an error. This checklist ends with rules you have read, tested in the emulator and probed from outside.

By the VibeSecurity team5 min read

Where the trust boundary is

The config object in your front end, including the API key, is public by design. Firebase's own documentation says these keys only identify the project and app, and that authorization is handled by Google Cloud IAM, Security Rules and App Check. So the question is never whether the key is visible. It is whether a request carrying that key is allowed by your rules.

Rules run on Google's servers for every client request. They do not apply to the Admin SDK, which runs with elevated privileges on your server, so anything you do there needs its own authorization checks.

The checklist

Firebase review areas
AreaWhat to verifyHow to test on your own projectPass condition
Firestore rulesNo allow read, write: if true; access tied to request.auth.uidRead firestore.rules; run the outside request in Step 2Unauthenticated request returns a permission error
Realtime Database rulesNo true at a parent path that grants access to private childrenRead database.rules.json; request the .json endpoint logged outPermission denied on private paths
Storage rulesPaths keyed to user id, write requires auth, sizes and types limitedRead storage.rules; list the bucket without credentialsListing and private reads are denied
App CheckEnforcement is on for each product you useCheck the App Check console for each service's enforcement stateEnforced, not just monitoring
AuthOnly the sign-in providers you intend are enabled; authorized domains are yoursReview Authentication settingsNo unused providers; no stray domains
API keysKey restricted to the APIs and apps you use; separate keys for non-Firebase APIsOpen Credentials in Google Cloud Console and read each key's restrictionsApplication and API restrictions set

Step 1: Read the rules like an attacker

Open your rules files or the Rules tab for each product. Firebase's documentation calls out the patterns that expose data: allow read, write: if true, and checks that only require request.auth != null, which lets any signed-in user touch everything. Test mode rules that expire also break apps abruptly, and blanket denials block clients, so neither is a fix.

Tie every rule to ownership or a role. Custom claims set with the Admin SDK are readable as request.auth.token, which avoids an extra document read for role checks.

Firestore ownership rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /notes/{noteId} {
      allow read, delete: if request.auth != null
        && request.auth.uid == resource.data.author_uid;
      allow create: if request.auth != null
        && request.auth.uid == request.resource.data.author_uid;
    }
  }
}

Step 2: Probe from outside

Run these against your own project with no credentials. Each should return a permission error. A JSON body with documents, keys or object names means the rules are open.

Unauthenticated requests to your own project
curl -s "https://firestore.googleapis.com/v1/projects/PROJECT_ID/databases/(default)/documents/notes?pageSize=1"

curl -s "https://PROJECT_ID-default-rtdb.firebaseio.com/.json?shallow=true"

curl -s "https://firebasestorage.googleapis.com/v0/b/BUCKET/o?maxResults=1"

Step 3: Realtime Database cascading

In Realtime Database, a child rule cannot take away access granted by a parent. If /users allows reads, setting read to false on a child does nothing. Grant narrowly at the deepest path instead, and keep private data under a path keyed by user id.

Per-user Realtime Database rules
{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth.uid === $uid",
        ".write": "auth.uid === $uid"
      }
    }
  }
}

Step 4: Storage rules

Match uploads to a path containing the user id and require a signed-in user to write. Public read is fine for avatars and wrong for documents, so split them by path. Add limits on content type and size for uploads, since rules can inspect request.resource.

Storage rules keyed by user
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/{fileName} {
      allow read: if request.auth != null && request.auth.uid == userId;
      allow write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

Step 5: Test in the emulator, then deploy

Firebase recommends the Local Emulator Suite and the @firebase/rules-unit-testing library for validating rules before production. Write at least three tests per collection: owner allowed, other user denied, signed-out denied. Run them with emulators:exec so they can run in CI.

Run and deploy
firebase emulators:exec --only firestore "npm test"
firebase deploy --only firestore:rules

Step 6: App Check, Auth and key restrictions

App Check attests that requests come from your genuine app, using providers such as Play Integrity, App Attest or reCAPTCHA Enterprise. It supports Firestore, Realtime Database, Storage and callable Cloud Functions. Firebase is explicit that it complements Authentication and Rules rather than replacing them: it protects your backend from abuse, not your users' data from other users. Turn on enforcement once your app sends tokens.

In Authentication, disable providers you do not use and check the authorized domains list. In Google Cloud Console, open Credentials and confirm each key has API restrictions and application restrictions (bundle ID, package name or referrer). Use a separate key for any non-Firebase Google API.

Common mistakes

  • Leaving the initial test-mode or if true rule in place.
  • Believing that a hidden API key is what protects the database.
  • Checking only request.auth != null, so any account can read everyone's data.
  • Setting a Realtime Database parent rule to true and expecting child rules to override it.
  • Turning on App Check in monitoring mode and never enforcing it.
  • Trusting client-supplied fields such as role or author_uid without comparing them to request.auth.

Keep it working

Keep rules in version control, run the emulator tests on every change, and repeat the outside requests after each deploy. When an assistant edits rules to fix a permission error, read the diff before deploying.

Frequently asked questions

Is my Firebase API key a secret?

No. Firebase documents these keys as identifiers for your project and app, safe to include in client code when restricted. Protection comes from Security Rules, App Check and IAM. Restrict the key anyway, and never reuse it for unrelated paid APIs.

Does App Check replace security rules?

No. App Check verifies the app and device making a request, while rules decide what a user may read or write. You need both. App Check reduces abuse of your quota and backend, rules protect the data.

Do rules apply to my server code?

Not to the Admin SDK, which bypasses them. If your server writes on behalf of users, verify the user's ID token and check ownership in your own code before touching data.

How do I know my rules work?

Write emulator tests for owner, other user and signed-out cases, and run the unauthenticated requests in this checklist against production. Both should agree that private data is denied.

Sources

  1. 1.Firebase docs: Fix insecure rules
  2. 2.Firebase docs: Security Rules and Authentication
  3. 3.Firebase docs: Learn about using API keys
  4. 4.Firebase docs: App Check
  5. 5.Firebase docs: Test rules with the Emulator Suite