HIGH brute force attackfirestore

Brute Force Attack in Firestore

How Brute Force Attack Manifests in Firestore

Brute force attacks in Firestore typically target authentication endpoints and data enumeration through predictable document IDs. Unlike traditional SQL databases where attackers can use UNION queries or information_schema tables, Firestore's NoSQL structure creates different attack surfaces.

The most common Firestore brute force pattern involves credential stuffing attacks against Firebase Authentication. Attackers use lists of compromised credentials to repeatedly attempt sign-ins, exploiting Firebase's default rate limiting configurations. A typical attack might look like:

POST /identitytoolkit/v1/relyingparty/verifyPassword?key=YOUR_API_KEY
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "password123",
  "returnSecureToken": true
}

Without proper rate limiting, attackers can make thousands of attempts per minute. The Firebase Authentication API doesn't enforce strict rate limits by default, making it vulnerable to credential stuffing campaigns.

Another Firestore-specific brute force vector targets collection enumeration. Since Firestore uses predictable document IDs (often auto-generated with incremental patterns or timestamps), attackers can enumerate collections by systematically guessing document IDs:

// Vulnerable enumeration pattern
const db = firebase.firestore();
const collection = db.collection('users');
const batchSize = 100;

async function bruteForceEnumeration() {
  for (let i = 0; i < 1000; i++) {
    const snapshot = await collection
      .where('__name__', 'in', generateBatchIds(i, batchSize))
      .get();
    
    snapshot.forEach(doc => {
      console.log(doc.id, doc.data());
    });
  }
}

Firestore's security rules can also be exploited through brute force if they're overly permissive. Consider rules that allow any authenticated user to read any document:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read: if request.auth != null;
      allow write: if false;
    }
  }
}

This allows attackers who obtain any valid Firebase token to enumerate entire collections. The predictable document ID generation (often using Firestore's auto-ID system or sequential patterns) makes enumeration trivial.

API key brute force is another critical vector. Firebase project API keys are often exposed in client-side code. Attackers can use these keys with different authentication methods to bypass security controls:

// API key brute force attempt
const apiKey = 'AIza...';
const authDomain = 'your-app.firebaseapp.com';

async function attemptLogin(email, password) {
  const response = await fetch(`https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${apiKey}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password, returnSecureToken: true })
  });
  return response.json();
}

Without proper backend validation and rate limiting, these attacks can continue indefinitely, potentially exposing user data or allowing unauthorized access to Firestore documents.

Firestore-Specific Detection

Detecting brute force attacks in Firestore requires monitoring both authentication attempts and data access patterns. For authentication brute force, Firebase Authentication provides audit logs that show repeated failed login attempts:

const auth = firebase.auth();
let failedAttempts = 0;
const MAX_ATTEMPTS = 5;
const LOCKOUT_DURATION = 300000; // 5 minutes

auth.onAuthStateChanged(user => {
  if (user) {
    failedAttempts = 0;
  }
});

async function signInWithEmail(email, password) {
  try {
    await auth.signInWithEmailAndPassword(email, password);
    failedAttempts = 0;
  } catch (error) {
    failedAttempts++;
    if (failedAttempts >= MAX_ATTEMPTS) {
      console.warn(`Account locked due to ${failedAttempts} failed attempts`);
      // Implement lockout logic
    }
    return { success: false, error: error.message };
  }
}

For data enumeration attacks, monitoring query patterns is essential. Firestore's query logs can reveal suspicious enumeration behavior:

const suspiciousPatterns = [
  { pattern: /where.*__name__.*in/i, threshold: 100 },
  { pattern: /collection.*users/i, threshold: 50 },
  { pattern: /orderBy.*id/i, threshold: 75 }
];

function analyzeQuery(query) {
  const queryStr = query.toString();
  for (const pattern of suspiciousPatterns) {
    if (pattern.pattern.test(queryStr) && query.count > pattern.threshold) {
      console.warn('Potential enumeration detected:', queryStr);
      return true;
    }
  }
  return false;
}

middleBrick's automated scanning can detect Firestore-specific brute force vulnerabilities by testing authentication endpoints and analyzing security rules. The scanner identifies:

Detection Type Method Risk Level
Authentication Brute Force Simulated credential stuffing attempts Critical
Data Enumeration Systematic document ID guessing High
Security Rule Analysis Security rules evaluation High
API Key Exposure Client-side code analysis Medium

The middleBrick CLI can scan your Firebase project's exposed endpoints:

npx middlebrick scan https://your-app.firebaseio.com

# Output includes:
# - Authentication endpoint vulnerabilities
# - Security rule weaknesses
# - Data exposure risks
# - API key exposure detection

Cloud Functions can also be monitored for brute force patterns. Implementing request throttling at the Cloud Function level provides an additional security layer:

const functions = require('firebase-functions');
const { RateLimiterMemory } = require('rate-limiter-flexible');

const limiter = new RateLimiterMemory({
  points: 10, // 10 requests
  duration: 60 // per 60 seconds
});

exports.protectedEndpoint = functions.https.onRequest(async (req, res) => {
  try {
    await limiter.consume(req.ip);
    // Process request
  } catch (rejRes) {
    res.status(429).send('Too many requests');
  }
});

Firestore-Specific Remediation

Remediating brute force vulnerabilities in Firestore requires a multi-layered approach. Start with Firebase Authentication security configurations:

// Enhanced authentication with rate limiting
const admin = require('firebase-admin');
const rateLimit = require('express-rate-limit');

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit each IP to 5 requests per windowMs
  message: 'Too many authentication attempts'
});

// Custom Firebase Auth middleware
async function secureAuth(req, res, next) {
  try {
    const { email, password } = req.body;
    
    // Check for common password patterns
    if (isCommonPassword(password)) {
      return res.status(400).json({
        error: 'Password too common'
      });
    }
    
    // Rate limiting per email address
    const attemptKey = `auth_attempt:${email}`;
    const currentAttempts = await getAuthAttempts(attemptKey);
    
    if (currentAttempts > 3) {
      return res.status(429).json({
        error: 'Too many attempts. Try again later.'
      });
    }
    
    await incrementAuthAttempt(attemptKey);
    next();
  } catch (error) {
    next(error);
  }
}

For Firestore security rules, implement principle of least privilege and prevent enumeration:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Only allow access to documents the user owns
    match /users/{userId}/data/{document=**} {
      allow read, write: if request.auth.uid == userId;
    }
    
    // Prevent collection enumeration
    match /users/{userId} {
      allow read: if request.auth.uid == userId;
      allow write: if request.auth.uid == userId;
    }
    
    // Public data must be explicitly allowed
    match /public/{document=**} {
      allow read: if true;
      allow write: if false;
    }
    
    // Prevent wildcard access to all documents
    match /{document=**} {
      allow read: if false;
      allow write: if false;
    }
  }
}

Implement document ID randomization to prevent enumeration attacks:

import { v4 as uuidv4 } from 'uuid';

// Instead of using predictable IDs
async function createUserSecurely(userData) {
  const userId = uuidv4(); // Random UUID instead of auto-increment
  const userRef = db.collection('users').doc(userId);
  
  await userRef.set({
    id: userId,
    email: userData.email,
    createdAt: admin.firestore.Timestamp.now(),
    // ... other fields
  });
  
  return userId;
}

// For existing collections with predictable IDs
async function migrateToRandomIds() {
  const usersRef = db.collection('users');
  const snapshot = await usersRef.get();
  
  const batch = db.batch();
  snapshot.forEach(doc => {
    const newId = uuidv4();
    const newRef = db.collection('users').doc(newId);
    batch.set(newRef, { ...doc.data(), id: newId });
    batch.delete(doc.ref);
  });
  
  await batch.commit();
}

Implement exponential backoff for failed authentication attempts:

const failedAttempts = new Map();
const lockouts = new Map();

async function secureSignIn(email, password) {
  const now = Date.now();
  const key = `auth:${email}`;
  
  // Check if account is temporarily locked
  if (lockouts.has(key)) {
    const lockout = lockouts.get(key);
    if (now - lockout.timestamp < lockout.duration) {
      return { error: 'Account temporarily locked' };
    } else {
      lockouts.delete(key);
    }
  }
  
  // Check failed attempts
  let attempts = failedAttempts.get(key) || { count: 0, lastAttempt: 0 };
  
  // Reset if enough time has passed
  if (now - attempts.lastAttempt > 15 * 60 * 1000) {
    attempts = { count: 0, lastAttempt: now };
  }
  
  // Verify credentials
  try {
    const user = await admin.auth().getUserByEmail(email);
    // Simulate credential verification
    if (password !== 'correct-password') {
      attempts.count++;
      failedAttempts.set(key, attempts);
      
      // Exponential backoff: 1s, 2s, 4s, 8s, 16s, then lockout
      const delay = Math.pow(2, attempts.count - 1) * 1000;
      if (attempts.count >= 6) {
        lockouts.set(key, { timestamp: now, duration: 5 * 60 * 1000 });
        return { error: 'Account locked for 5 minutes' };
      }
      
      return { error: `Invalid credentials. Try again in ${delay/1000} seconds`, delay };
    }
    
    // Successful login
    failedAttempts.delete(key);
    return { success: true, user };
  } catch (error) {
    return { error: error.message };
  }
}

Frequently Asked Questions

How can I tell if my Firestore API is being targeted by brute force attacks?

Monitor your Firebase Authentication logs for repeated failed login attempts from the same IP address or targeting the same user account. Look for patterns like 10+ failed attempts within a short timeframe, or multiple attempts using common passwords. Cloud Functions logs can reveal suspicious query patterns, and middleBrick's automated scanning can identify authentication endpoints vulnerable to credential stuffing without requiring any setup.

Does Firestore's built-in security handle brute force protection automatically?

No, Firestore's security rules only control data access permissions—they don't provide rate limiting or brute force protection. You need to implement additional layers like Firebase Authentication's built-in protections (which have configurable limits), Cloud Functions with rate limiting middleware, and custom security rules that prevent enumeration. middleBrick can scan your configuration to identify missing protections that leave your Firestore endpoints vulnerable to credential stuffing and data enumeration attacks.