Skip to content

10 Common Firebase Security Vulnerabilities and How to Fix Them

After scanning hundreds of Firebase applications, we've identified recurring security vulnerabilities that put user data at risk. This guide covers the 10 most common Firebase security issues and shows you exactly how to fix them.

1. World-Readable Realtime Database

The Vulnerability

The most critical issue we find: databases with .read: true in root rules, making all data publicly accessible.

Vulnerable code:

json
{
  "rules": {
    ".read": true,
    ".write": "auth != null"
  }
}

Why it's dangerous: Anyone can download your entire database without authentication. User emails, personal data, and sensitive information are exposed.

The Fix

json
{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

Better yet, restrict to user-specific data:

json
{
  "rules": {
    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

How to Test

bash
firescan > scan --unauth

Should show "Permission denied" for sensitive paths.

2. Missing User Isolation in Firestore

The Vulnerability

Firestore rules that allow any authenticated user to read/write all documents.

Vulnerable code:

javascript
match /users/{userId} {
  allow read, write: if request.auth != null;
}

Impact: User A can read and modify User B's data.

The Fix

javascript
match /users/{userId} {
  allow read, write: if request.auth.uid == userId;
}

For shared collections with proper access control:

javascript
match /posts/{postId} {
  allow read: if request.auth != null;
  allow write: if request.auth.uid == resource.data.authorId;
}

3. Unprotected Cloud Functions

The Vulnerability

HTTP Cloud Functions with no authentication checks.

Vulnerable code:

javascript
exports.deleteUser = functions.https.onRequest((req, res) => {
  const userId = req.body.userId;
  admin.auth().deleteUser(userId);
  res.send("User deleted");
});

Impact: Anyone can call your functions and perform privileged operations.

The Fix

javascript
exports.deleteUser = functions.https.onCall(async (data, context) => {
  // Verify authentication
  if (!context.auth) {
    throw new functions.https.HttpsError('unauthenticated',
      'User must be authenticated');
  }

  // Verify authorization
  const uid = context.auth.uid;
  if (uid !== data.userId && !await isAdmin(uid)) {
    throw new functions.https.HttpsError('permission-denied',
      'Insufficient permissions');
  }

  await admin.auth().deleteUser(data.userId);
  return { success: true };
});

Use onCall instead of onRequest for automatic authentication handling.

4. Public Cloud Storage Buckets

The Vulnerability

Storage buckets allowing public read/write access.

Vulnerable code:

javascript
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}

Impact: Anyone can upload files to your bucket or download all stored files.

The Fix

javascript
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/{allPaths=**} {
      allow read, write: if request.auth.uid == userId;
    }

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

5. No Data Validation

The Vulnerability

Rules that don't validate data types or required fields.

Vulnerable code:

javascript
match /posts/{postId} {
  allow write: if request.auth != null;
}

Impact: Users can write arbitrary data, breaking your application or injecting malicious content.

The Fix

javascript
match /posts/{postId} {
  allow write: if request.auth != null
    && request.resource.data.keys().hasAll(['title', 'content', 'authorId'])
    && request.resource.data.title is string
    && request.resource.data.title.size() > 0
    && request.resource.data.title.size() <= 200
    && request.resource.data.content is string
    && request.resource.data.authorId == request.auth.uid;
}

6. Overly Permissive List Operations

The Vulnerability

Allowing users to list entire collections.

Vulnerable code:

javascript
match /users/{userId} {
  allow read: if request.auth != null;
}

Impact: Users can enumerate all documents in a collection, discovering other users, IDs, or sensitive paths.

The Fix

javascript
match /users/{userId} {
  allow get: if request.auth != null && request.auth.uid == userId;
  // No "list" permission granted
}

Or restrict list operations:

javascript
match /users/{userId} {
  allow list: if request.auth != null
    && request.query.limit <= 10
    && request.auth.token.admin == true;
}

7. Missing Write Validation

The Vulnerability

Allowing users to modify fields they shouldn't.

Vulnerable code:

javascript
match /users/{userId} {
  allow update: if request.auth.uid == userId;
}

Impact: Users can set themselves as admin or modify critical fields.

The Fix

javascript
match /users/{userId} {
  allow update: if request.auth.uid == userId
    && !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role', 'isAdmin', 'credits']);
}

Or whitelist allowed fields:

javascript
match /users/{userId} {
  allow update: if request.auth.uid == userId
    && request.resource.data.diff(resource.data).affectedKeys().hasOnly(['displayName', 'photoURL', 'bio']);
}

8. Timestamp Manipulation

The Vulnerability

Allowing users to set their own timestamps.

Vulnerable code:

javascript
match /posts/{postId} {
  allow create: if request.auth != null
    && request.resource.data.authorId == request.auth.uid;
}

Impact: Users can backdate or future-date content, manipulate sorting, or bypass time-based restrictions.

The Fix

javascript
match /posts/{postId} {
  allow create: if request.auth != null
    && request.resource.data.authorId == request.auth.uid
    && request.resource.data.createdAt == request.time;
}

9. Weak Query Security

The Vulnerability

Not restricting query parameters in rules.

Vulnerable code:

javascript
match /posts/{postId} {
  allow list: if request.auth != null;
}

Impact: Users can query all data without restrictions, bypassing intended access controls.

The Fix

javascript
match /posts/{postId} {
  allow list: if request.auth != null
    && request.query.limit <= 20
    && request.query.orderBy == 'createdAt';
}

10. Insecure Custom Claims

The Vulnerability

Setting custom claims based on client input.

Vulnerable code:

javascript
exports.setAdmin = functions.https.onCall(async (data, context) => {
  if (data.isAdmin) {
    await admin.auth().setCustomUserClaims(context.auth.uid, {
      admin: true
    });
  }
});

Impact: Any user can make themselves an admin.

The Fix

javascript
exports.setAdmin = functions.https.onCall(async (data, context) => {
  // Verify caller is already an admin
  if (!context.auth.token.admin) {
    throw new functions.https.HttpsError('permission-denied');
  }

  // Additional verification
  const targetUser = await admin.auth().getUser(data.targetUid);
  if (!targetUser.email.endsWith('@company.com')) {
    throw new functions.https.HttpsError('invalid-argument');
  }

  await admin.auth().setCustomUserClaims(data.targetUid, {
    admin: true
  });
});

How to Find These Vulnerabilities

Manual Review

Review your security rules file by file, checking for:

  • Public read/write access
  • Missing user isolation
  • No data validation
  • Unrestricted queries

Automated Scanning with FireScan

FireScan automatically tests for these vulnerabilities:

bash
# Install
go install github.com/JacobDavidAlcock/firescan/cmd/firescan@latest

# Configure
firescan
firescan > set projectID your-project
firescan > set apiKey AIza...

# Test unauthenticated access
firescan > scan --unauth

# Test authenticated access
firescan > auth --create-account
firescan > scan --all

Testing Best Practices

  1. Test before deploying - Always test rules changes before production
  2. Test multiple authentication states - Unauthenticated, authenticated, admin
  3. Test edge cases - Empty values, special characters, maximum sizes
  4. Use automated tools - Catch issues humans miss
  5. Regular audits - Re-test periodically as your app evolves

Quick Security Checklist

  • [ ] No world-readable databases
  • [ ] User data isolated by UID
  • [ ] Cloud Functions require authentication
  • [ ] Storage rules restrict access
  • [ ] Data validation on all writes
  • [ ] List operations restricted
  • [ ] Critical fields protected from modification
  • [ ] Timestamps server-controlled
  • [ ] Query parameters validated
  • [ ] Custom claims properly secured

Next Steps

Don't let these common vulnerabilities compromise your Firebase application. Test regularly and fix issues before attackers find them.


Need help securing your Firebase app? Get a professional security assessment or support FireScan development.