IS FIREBASE SAFE? SECURITY ANALYSIS | VIBEEVAL

Firebase infrastructure is solid. Most Firebase apps are not — open Security Rules, public Storage, and Admin SDK abuse paths ship with default AI scaffolds.

TEST YOUR FIREBASE APP NOW

Enter your live URL — we exercise Firestore/Storage exposure patterns and report what an anonymous client can read or write.

Security Rules are Critical

Firebase Security Rules are the only barrier between your data and the public internet. Unlike traditional databases behind a server, Firebase is directly accessible from clients. Misconfigured rules expose all your data. The Firebase config in your bundle includes the API key and project ID by design — they are not secrets, they are identifiers. The secret is your rules logic.

The pattern we see most often: an AI generator scaffolds a Firebase app, writes Firestore reads against users/{userId}, and never publishes a firestore.rules file. The default rules from the console — set to “test mode” with a 30-day timer — silently expire to deny-all, the app breaks, the developer pastes allow read, write: if true; into the rules and ships. Now everyone can read everyone’s data forever.

That path is not hypothetical. It is the modal Firebase finding in AI-built apps: the demo needed open rules, production inherited them, and App Check / Auth were assumed to “cover it” without ever being wired into the rules expressions.

Common Security Issues

Open Security Rules

Many apps launch with rules that allow all reads and writes. This is the default for development but catastrophic in production:

// DANGEROUS - default test mode after expiry
match /{document=**} {
  allow read, write: if true;
}

The {document=**} recursive wildcard means every collection, every document, every subcollection. The whole project is public.

Slightly less obvious variants still fail:

// Still open to the world after the date
allow read, write: if request.time < timestamp.date(2030, 1, 1);

// "Logged in" only — any account reads any user
allow read, write: if request.auth != null;

Ship deny-by-default, then open known paths with ownership checks. Never keep a catch-all if true “for debugging” in the same file as production matches.

Rule Logic Errors

Complex Security Rules syntax leads to logical errors that create unintended access paths. The most common mistakes:

// BAD: only checks user is logged in, not whose data
match /users/{userId} {
  allow read: if request.auth != null;
}

// GOOD: scope to the requesting user
match /users/{userId} {
  allow read: if request.auth != null
              && request.auth.uid == userId;
}

Equally dangerous: allow write: if request.auth.uid == request.resource.data.owner — the user can write any value to owner, including their own UID, and immediately own the document.

Ownership on create must bind request.resource.data.owner (or equivalent) to request.auth.uid. Ownership on update/delete must read resource.data.owner (existing doc), not only the request payload. Mixing those up is a classic privilege flip.

Rule Evaluation Order

Firestore evaluates allow rules with OR semantics across match blocks. A permissive rule on a parent path overrides a stricter rule on a child:

// Outer rule grants read; inner rule cannot revoke it
match /posts/{post} {
  allow read: if true;
  match /comments/{c} {
    allow read: if request.auth != null;  // useless
  }
}

Always write rules tightest at the root and only widen explicitly. Prefer separate top-level collections with explicit matches over deep trees of “open parent, locked child” hope.

Exposed Configuration

Firebase config in client code reveals project details. While normal, it emphasizes the need for proper Security Rules. The danger is not the config — it is the developer who hides the config thinking it adds security, then writes loose rules because “the API key is hidden anyway”.

Also separate the web API key (public, in firebaseConfig) from service account JSON (admin, never in a browser). AI codegen sometimes imports firebase-admin into a client component “to create users from the form,” embedding the service account. That is a full project compromise, not a rules issue — rotate immediately and remove admin from the client graph. Use Token Leak Checker on the deployed bundle.

Missing Validation

Security Rules should validate data structure and content, but this is often skipped. A rule like allow create: if request.auth.uid == request.resource.data.owner lets the user write any other field — including isAdmin: true if your app reads admin status from Firestore. Validate the shape:

allow create: if request.auth.uid == request.resource.data.owner
              && !('isAdmin' in request.resource.data)
              && request.resource.data.keys().hasOnly(['owner', 'title', 'body']);

Cap string lengths, enforce types (is string, is number), and reject unknown keys. Rules are your last schema check when clients write directly.

Storage Rules Forgotten

Firestore rules and Storage rules are separate files and separate deploys. Locking down Firestore does nothing for Storage. Storage’s default test-mode rule is just as open as Firestore’s, and AI generators forget the Storage file constantly.

// In storage.rules
match /uploads/{userId}/{file} {
  allow read, write: if request.auth.uid == userId
    && request.resource.size < 5 * 1024 * 1024
    && request.resource.contentType.matches('image/.*');
}

Also decide which paths are truly public (marketing images) vs private (IDs, invoices). Do not mix them under one open prefix. Strip EXIF if avatars can leak location; that is app logic, but Storage rules at least stop anonymous listing of private prefixes.

Realtime Database Cascading Rules

Realtime Database rules inherit downward — a .read: true at the root grants read on every node below. Many apps set a permissive root rule “for now” and forget. Audit database.rules.json from the root down, not from the leaves up.

{
  "rules": {
    ".read": false,
    ".write": false,
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid",
        ".write": "auth != null && auth.uid == $uid"
      }
    }
  }
}

Cloud Functions as Admin SDK bypass

Any Cloud Function using the Admin SDK bypasses Security Rules. That is correct for privileged jobs — dangerous when the function is just a proxy for client input:

// BAD — path from user, admin read
exports.getDoc = functions.https.onCall(async (data, context) => {
  if (!context.auth) throw new functions.https.HttpsError("unauthenticated", "login");
  return admin.firestore().doc(data.path).get().then((s) => s.data());
});

// BETTER — fixed collection, ownership check, least fields
exports.getMyProfile = functions.https.onCall(async (_data, context) => {
  if (!context.auth) throw new functions.https.HttpsError("unauthenticated", "login");
  const uid = context.auth.uid;
  const snap = await admin.firestore().doc(`users/${uid}`).get();
  return snap.data();
});

Callable and HTTPS functions also need abuse controls: App Check, rate limits, payload size limits, and never reflecting stack traces. A Function that “lists all users for the admin dashboard” without verifying a custom claim is an open directory.

App Check skipped

App Check reduces abuse from scripts that are not your real app. It is not authorization — a determined attacker can still call with a real app install — but it stops anonymous bulk scraping with curl. Enable App Check on Firestore, Storage, and Functions for production.

Wire enforcement gradually: monitor mode first, then enforce, so legitimate clients with broken attestation do not brick production without a signal.

Auth token claims you forget to check

Firebase Auth issues ID tokens with claims. Rules can read request.auth.token.email_verified, custom claims (admin), and sign-in provider. High-trust actions should require verification:

allow update: if request.auth != null
  && request.auth.token.email_verified == true
  && request.auth.uid == userId;

Without that, a throwaway account with an unowned email can still pass request.auth != null checks. Combine with rate limits on account creation for consumer apps.

Security Assessment

Strengths

    • Google-grade infrastructure security
    • Built-in authentication with multiple providers
    • Security Rules provide granular access control
    • Automatic HTTPS and TLS encryption
    • SOC 2, ISO 27001 compliance
    • App Check protects against abuse from non-app clients
    • Firebase Emulator Suite for offline rule testing
    • Custom claims for server-set roles

Concerns

    • Security Rules often misconfigured or disabled
    • Default rules may allow public read/write
    • Client-side SDK exposes configuration
    • Complex rule syntax leads to errors
    • No RLS - relies entirely on Security Rules
    • Storage and Firestore rules are separate, easy to forget one
    • Cloud Functions with Admin SDK bypass all rules
    • Realtime Database rules cascade in surprising ways
    • AI scaffolds optimize for “it works” over “it is locked”

Firebase vs Supabase: rule model comparison

Firebase and Supabase solve the same problem — direct client access to a database — with different rule languages.

  • Firebase Security Rules are a path-based DSL with match blocks. Compact for per-document access. Powerful with get() and exists() for cross-document checks, though those count as billable reads.
  • Supabase RLS uses Postgres CREATE POLICY statements. Full SQL expressiveness — joins, subqueries, custom functions. Per-table, not per-row-path.

Both default to deny-all once enabled. Both can be defeated by an over-permissive rule. The most common mistake is identical: a rule that checks “is the user logged in” instead of “is this row owned by the logged-in user”.

See Is Supabase Safe? for the Postgres-side twin of this guide.

Multi-tenant patterns that fail in Firebase

SaaS apps often model orgs as orgs/{orgId}/... with membership documents. Failure modes:

  • Rules check request.auth != null but not membership in the org.
  • Membership docs are client-writable (allow write: if request.auth.uid == userId on members/{userId}), so anyone can add themselves to any org.
  • Cross-document get() checks exist but omit null/missing membership denial.
  • List queries on org collections allowed without a constraint rules can prove.

Safer pattern: set org membership via Cloud Functions (Admin SDK) after invite acceptance; rules only read membership with exists() / get() and deny client writes to membership paths.

function isOrgMember(orgId) {
  return request.auth != null
    && exists(/databases/$(database)/documents/orgs/$(orgId)/members/$(request.auth.uid));
}

match /orgs/{orgId}/projects/{projectId} {
  allow read: if isOrgMember(orgId);
  allow write: if isOrgMember(orgId)
    && request.resource.data.keys().hasOnly(['name', 'updatedAt', 'ownerUid']);
}

How to verify rules before launch

  1. Unit tests with @firebase/rules-unit-testing — assert allow/deny for owner, other user, and unauthenticated.
  2. Rules Playground in the Firebase console for quick ad-hoc checks (not a substitute for tests).
  3. Live probe: from an anonymous client, try getDoc / list on every collection path your app uses.
  4. Cross-tenant: authenticated as user B, request user A’s document by ID.
  5. Storage: attempt download of another user’s path.
  6. Functions: call every HTTPS/callable with missing auth and with another user’s IDs.
  7. Run Firebase Scanner / VibeEval against the deployed app.
// rules-unit-testing sketch
import { assertFails, assertSucceeds } from "@firebase/rules-unit-testing";

await assertFails(unauth.firestore().doc("users/alice").get());
await assertSucceeds(alice.firestore().doc("users/alice").get());
await assertFails(bob.firestore().doc("users/alice").get());

Expand tests when you add collections — AI feature work is the usual regression moment. A CI job that boots the emulator and runs the rules suite on every PR is cheap insurance.

Common mistakes in AI-generated Firebase apps

  • Shipping console test mode rules past the expiry date.
  • request.auth != null without UID ownership.
  • Admin flags writable by clients (isAdmin in document fields).
  • Storage still open while Firestore looks locked.
  • Service account JSON committed or embedded in a web bundle.
  • Callable functions that accept arbitrary collection paths.
  • No email_verified check for high-trust actions.
  • Relying on hiding the config instead of rules.
  • Using Realtime Database with a root .read: true “for the chat demo.”
  • Client SDK list queries that dump entire collections once any doc is readable.
  • Custom claims never set, roles stored in editable user docs.

Cost and abuse angles (not only data leaks)

Open rules are not only a confidentiality problem. Attackers can:

  • Write huge documents until you hit storage bills.
  • Create millions of auth users against projects without App Check.
  • Trigger Functions with spam callables.
  • Host malware files on open Storage and abuse your domain reputation.

App Check + rules + quotas + billing alerts belong on the same launch checklist as ownership checks.

Pre-launch checklist (10 minutes)

  1. firestore.rules and storage.rules committed and deployed.
  2. No match /{document=**} { allow read, write: if true; } anywhere.
  3. Every user-data path compares request.auth.uid to the resource owner.
  4. Creates/updates validate keys and forbid privilege fields.
  5. App Check enabled on production.
  6. No service account material in the client bundle (Token Leak Checker).
  7. Functions re-check auth and never trust client-supplied paths for admin reads.
  8. Emulator tests for owner / stranger / anon on each collection.
  9. Dynamic scan on the live URL.
  10. Billing alerts on for unexpected write/read spikes.

The Verdict

Firebase is safe as a platform with Google’s security backing. However, the security of your Firebase application depends entirely on your Security Rules configuration. Test rules thoroughly using the Firebase Emulator and Rules Playground before deployment. Never deploy with default open rules.

Four checks before production:

  1. firestore.rules and storage.rules are both committed to source control and deployed.
  2. No match /{document=**} { allow read, write: if true; } block exists anywhere.
  3. Every allow rule that touches user data references request.auth.uid and compares it to a field in the document.
  4. App Check is enabled on production, blocking calls that don’t come from your verified app.

Add a fifth if you use Cloud Functions: no Admin SDK path constructed from raw client input.

How to Secure Firebase

Step-by-step security guide covering rule patterns, App Check, and Cloud Functions hardening.

Firebase Security Checklist

Interactive security checklist with the rule snippets you can paste in.

Firebase Studio Security Scanner

Run a full security scan against your live Firebase project that probes every collection for rule bypass.

Token Leak Checker

Find Firebase Admin SDK keys or service account JSONs that shipped to a public surface.

Firebase Security Rules: common mistakes

Deep dive into rule patterns that fail in production.

Rules review agenda (30 minutes)

  1. Open firestore.rules + storage.rules side by side.
  2. Highlight every allow without request.auth.uid comparison.
  3. List subcollections missing matches.
  4. Run emulator tests.
  5. Probe production with anon client.
  6. File tickets for every gap.

Custom claims vs document-stored roles

AI scaffolds often store role: "admin" on the user document and check it in the client. That is mass assignment waiting to happen. Prefer custom claims set only via Admin SDK after a verified server workflow:

// Cloud Function — after invite acceptance / staff provisioning
await admin.auth().setCustomUserClaims(uid, { admin: true, orgId });

// firestore.rules
allow read: if request.auth.token.admin == true
  && request.auth.token.orgId == orgId;

Claims lag until the client refreshes the ID token (getIdToken(true)). Document that in ops runbooks so “I just got admin but rules deny me” does not turn into a rules-loosening incident.

Never allow clients to write admin, role, or orgId fields that rules later treat as trust anchors.

List queries and “rules cannot prove constraint”

Firestore rejects list queries unless rules can prove every returned document is allowed. Developers “fix” this by opening list access:

// BAD — any signed-in user lists all projects
match /projects/{id} {
  allow list: if request.auth != null;
}

Prefer queries that include the constraint rules expect (where("ownerUid", "==", request.auth.uid)) and write rules that require that field equality. If the product needs org-wide lists, gate with isOrgMember(orgId) and require orgId in the query.

When AI adds a new collection for “admin dashboard,” it often enables broad list reads. That is a full-directory leak of every customer project name.

Emulator CI pattern

# sketch — GitHub Actions
- run: npm ci
- run: firebase emulators:exec --only firestore,auth "npm run test:rules"

test:rules should cover at least: unauthenticated deny, owner allow, peer deny, admin claim allow, membership edge cases, Storage path peer deny. Fail the PR if coverage for a new match path is missing — generators add collections faster than humans notice.

Incident response for open Firebase rules

  1. Deploy deny-by-default or ownership-locked rules immediately (do not only “disable the app”).
  2. Enable/force App Check if not already enforcing.
  3. Rotate service accounts and OAuth client secrets if Admin SDK material may have leaked.
  4. Export Auth + Firestore audit/access logs for the open window; estimate accessible collections.
  5. Notify users under LGPD/GDPR/NDB if personal data was world-readable — accessibility often equals notifiability.
  6. Add emulator tests that would have caught the open rule; rescan with Firebase Scanner.

Open Storage with identity documents is the same severity class as open Firestore profiles — include Storage in the blast-radius table.

firebaseConfig is not a secret (again, with ops implications)

Hiding apiKey behind a backend proxy without fixing rules does nothing useful and often breaks Auth. Restrict API keys in Google Cloud Console (HTTP referrers, APIs enabled) as defense-in-depth against quota theft — that is not row security. Row security is only Rules (+ App Check for abuse).

When to choose Firestore vs RTDB for new AI apps

Realtime Database cascading rules make accidental root opens more likely; Firestore path matches are easier to audit collection-by-collection. For greenfield AI scaffolds, prefer Firestore + Storage + Auth unless you have a hard RTDB latency reason. If you inherit RTDB, audit from the root .read/.write downward before any feature work.

Firebase Studio / AI scaffolding notes

Firebase Studio and similar generators produce working clients quickly. Watch for:

  • Test-mode rules committed “to unblock the demo”
  • Storage rules never generated while Firestore rules look fine
  • Callable functions that accept collection paths as strings
  • Admin SDK used from a “server component” that is still bundled for the client

After any AI session that touches data paths, run emulator tests + Firebase Scanner on the live URL. The platform is not the weak link; the generated rules file is.

Cross-linking to Supabase mental model

If your team also ships Lovable/Supabase apps, map:

Firebase Supabase
Security Rules RLS policies
request.auth.uid auth.uid()
Admin SDK service_role
App Check (rate limits + bot controls; different mechanism)
Storage rules file storage.objects policies

Same failure: “logged in” without “owns this resource.” See Is Supabase Safe?.

Field validation recipes (Firestore)

function isValidProject() {
  let data = request.resource.data;
  return data.keys().hasOnly(['name', 'ownerUid', 'createdAt', 'updatedAt'])
    && data.name is string
    && data.name.size() > 0
    && data.name.size() < 120
    && data.ownerUid == request.auth.uid
    && data.createdAt is timestamp;
}

match /projects/{id} {
  allow create: if request.auth != null && isValidProject();
  allow update: if request.auth != null
    && resource.data.ownerUid == request.auth.uid
    && request.resource.data.ownerUid == resource.data.ownerUid
    && isValidProject();
  allow delete: if request.auth != null
    && resource.data.ownerUid == request.auth.uid;
  allow read: if request.auth != null
    && resource.data.ownerUid == request.auth.uid;
}

Note ownerUid immutable on update — blocks ownership transfer via client. AI-generated rules often omit that second comparison.

Billing and quota attacks

Open write rules enable document spam until you hit Firebase quotas or the bill. Pair rules lockdown with:

  • App Check enforcement
  • Budget alerts in Google Cloud
  • Rate limits on Auth signup
  • Disabled unused Auth providers

Confidentiality incidents get headlines; silent bill shock is the other open-rules failure mode.

Scan Your Firebase App

Let VibeEval check your Firebase application for security vulnerabilities. The scanner exercises both Firestore and Storage rules, attempts cross-tenant reads, and reports anything that came back when it shouldn’t have.

COMMON QUESTIONS

01
Is Firebase safe to use in production?
Yes, when Security Rules are written and tested. Google runs the infrastructure with strong defaults — encryption, identity, infrastructure isolation. The risk is the rules layer: most Firebase apps in the wild ship with rules that nominally check `request.auth != null` but never check that the data being read belongs to that user.
Q&A
02
Why is the Firebase config exposed in the browser?
It is meant to be. The `firebaseConfig` object contains the project ID and an API key that identifies your project to Google's auth servers. It is not a secret. The actual access control happens in Security Rules. Treating the config as a secret is wasted effort; treating Security Rules as the only barrier between data and the public internet is the correct mental model.
Q&A
03
What does a default-open Firestore rule look like and why is it dangerous?
It looks like `allow read, write: if true;` or — even more common — `allow read, write: if request.time < timestamp.date(2025, 1, 1);` from the Firebase console's 'test mode'. The first allows anyone in the world to read or write anything. The second was time-locked but the time has passed, leaving the rule permanently open. Both ship to production constantly.
Q&A
04
What is the difference between Firestore Rules and Realtime Database Rules?
Firestore Rules use a CEL-like syntax with `match` blocks per document path. Realtime Database Rules use a JSON tree mirroring the data. Realtime Database rules cascade down — a permissive rule at the root grants the same permission to every descendant unless overridden. This makes Realtime Database easier to accidentally open up at the root.
Q&A
05
Are Firebase Storage rules separate from Firestore rules?
Yes, and that is the trap. Locking down Firestore does not lock down Storage. Storage has its own ruleset with its own `match` blocks. AI generators that scaffold Firebase + image upload routinely set Firestore rules correctly and leave Storage on the test-mode default of `allow read, write: if request.time < ...`.
Q&A
06
Can Cloud Functions leak the Firebase Admin SDK?
Cloud Functions run server-side and have full Admin SDK access — which bypasses every Security Rule. The leak path is functions that take user input, call `db.collection(input).get()`, and return the result. The function is authenticated to Firebase as admin; the user is just typing a path. This is a complete bypass.
Q&A
07
How do I test Firebase Security Rules before deploying?
Use the Firebase Emulator Suite. Write rules, then write Jest or Mocha tests that use the `@firebase/rules-unit-testing` library to assert allowed and denied operations. Without rule tests, every deploy is a production change to your access control with no review trail.
Q&A
08
Does Firebase Auth's `email_verified` flag stop fake account creation?
Only if your rules check it. By default, anyone can create an account with any email and immediately use it — `email_verified` stays false until they click a link. Rules should require `request.auth.token.email_verified == true` for any operation that depends on an asserted identity.
Q&A

PROBE YOUR FIREBASE RULES LIVE

Catch expired test-mode rules, missing ownership checks, and open Storage buckets before attackers do. Free 14-day trial, no card.

14-day free trial · No credit card · Cancel anytime

SCAN MY FIREBASE APP