Firebase

Firebase Security Rules: The Definitive Guide

Comprehensive guide to writing secure Firebase rules

Last updated 2026-01-15

Understanding Firebase Security Rules

Firebase Security Rules are the gatekeepers to your Firestore, Realtime Database, and Cloud Storage data. Unlike traditional server-side authorization, these rules execute on Google's infrastructure and are the only barrier between your data and the public internet. A misconfigured rule can expose your entire database.

Firestore Rules Structure

Firestore rules follow a path-matching pattern:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Match a specific collection
    match /users/{userId} {
      allow read: if request.auth != null && request.auth.uid == userId;
      allow write: if request.auth != null && request.auth.uid == userId;
    }

    // Match subcollections
    match /users/{userId}/private/{document} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

The Danger of Open Rules

Firebase projects created for prototyping often start with completely open rules. These MUST be replaced before any production use:

// DANGEROUS: Anyone can read and write everything
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

Firebase sends email warnings about insecure rules, but many developers ignore them. Our scanning data shows approximately 30% of Firebase apps in production have overly permissive rules on at least one collection.

Core Rule Patterns

Authentication Check

The most basic rule ensures the user is logged in:

allow read: if request.auth != null;

However, this is often insufficient. Any authenticated user (including throwaway accounts) would gain access.

Owner-based Access

Restrict documents to their owner:

match /orders/{orderId} {
  allow read, update, delete: if request.auth != null
    && resource.data.userId == request.auth.uid;
  allow create: if request.auth != null
    && request.resource.data.userId == request.auth.uid;
}

Note: For create, the document does not yet exist so you use request.resource.data instead of resource.data.

Role-based Access with Custom Claims

Set custom claims on the server and reference them in rules:

match /admin/{document=**} {
  allow read, write: if request.auth != null
    && request.auth.token.admin == true;
}

Data Validation

Rules can validate the shape and content of incoming data:

match /posts/{postId} {
  allow create: if request.auth != null
    && request.resource.data.title is string
    && request.resource.data.title.size() > 0
    && request.resource.data.title.size() <= 200
    && request.resource.data.authorId == request.auth.uid
    && request.resource.data.createdAt == request.time;
}

Common Pitfalls

1. Wildcard Matches Override Specific Rules

Firestore rules are additive. If any allow rule matches, access is granted. A broad wildcard match can accidentally override restrictive rules on specific paths.

// BAD: The wildcard grants read to everything, overriding the users rule
match /{document=**} {
  allow read: if request.auth != null;
}
match /users/{userId} {
  allow read: if request.auth.uid == userId; // This is meaningless now
}

2. Not Validating Writes

Allowing writes without validating the data structure lets attackers inject arbitrary fields:

// BAD: Attacker can set { role: "admin", credits: 999999 }
match /users/{userId} {
  allow write: if request.auth.uid == userId;
}

// GOOD: Restrict which fields can be written
match /users/{userId} {
  allow update: if request.auth.uid == userId
    && request.resource.data.diff(resource.data).affectedKeys()
        .hasOnly(['displayName', 'photoURL', 'bio']);
}

3. Recursive Wildcards in the Wrong Place

{document=**} matches all documents in all subcollections. Use it sparingly and never at the top level with permissive rules.

4. Ignoring List vs. Get

Firestore distinguishes between get (single document) and list (collection query). Allowing list on a collection can enable data enumeration even if individual documents would be protected by query constraints.

match /users/{userId} {
  allow get: if request.auth.uid == userId;
  allow list: if request.auth != null
    && request.query.limit <= 10;
}

Realtime Database Rules

Realtime Database uses a JSON-based rule syntax:

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    },
    "public_posts": {
      ".read": true,
      "$postId": {
        ".write": "auth !== null && newData.child('authorId').val() === auth.uid",
        ".validate": "newData.hasChildren(['title', 'content', 'authorId'])"
      }
    }
  }
}

A critical difference: Realtime Database rules cascade downward. A .read: true rule on a parent path grants read access to all children, and it cannot be revoked by child rules.

Cloud Storage Rules

Storage rules protect uploaded files:

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/{allPaths=**} {
      allow read: if request.auth != null;
      allow write: if request.auth != null
        && request.auth.uid == userId
        && request.resource.size < 5 * 1024 * 1024
        && request.resource.contentType.matches('image/.*');
    }
  }
}

Always validate file size and content type in storage rules to prevent abuse.

Testing Rules

Use the Firebase Emulator Suite to test rules locally:

const { assertSucceeds, assertFails } = require('@firebase/rules-unit-testing');

it('should deny read to non-owner', async () => {
  const db = getFirestore({ uid: 'user-a' });
  await assertFails(db.collection('users').doc('user-b').get());
});

Automated Scanning

Manually reviewing rules for every collection is tedious and error-prone. AuditYour.app scans your Firebase project configuration to detect open rules, missing validation, and overly broad wildcard matches, then provides prioritized remediation steps.

Scan your app for this vulnerability

AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.

Run Free Scan