HIGH sql injectionfirestore

Sql Injection in Firestore

How Sql Injection Manifests in Firestore

Firestore Sql Injection exploits occur when untrusted user input is incorporated into Firestore queries without proper sanitization. Unlike traditional SQL databases, Firestore uses NoSQL document queries, but injection vulnerabilities still exist through query parameter manipulation.

The most common Firestore injection pattern involves constructing queries using string concatenation or interpolation. For example:

const collection = firestore.collection('users');
const query = collection.where('email', '==', userInput);

When userInput contains special characters or malicious operators, attackers can manipulate query logic. Firestore supports operators like '<', '>', '>=', '<=', 'array-contains', and 'in', which can be exploited if user input isn't validated.

Consider this vulnerable pattern:

const field = req.query.field; // User-controlled
const value = req.query.value; // User-controlled
const operator = req.query.operator || '==';

const query = firestore.collection('products')
  .where(field, operator, value);

An attacker could supply field=admin%20or%201%3D1 to potentially bypass authorization checks, though Firestore's query engine prevents some classic SQL injection patterns due to its document model.

More sophisticated attacks target Firestore's array operations:

// Vulnerable: array-contains injection
const tags = req.body.tags; // ['admin', 'user']
const query = firestore.collection('users')
  .where('tags', 'array-contains', tags);

If tags is an array, Firestore will match documents where any element matches, potentially expanding the result set beyond intended limits.

Firestore also has unique injection vectors through document path traversal:

// Vulnerable: path injection
const userId = req.params.userId;
const docRef = firestore.doc(`users/${userId}/settings/profile`);

Malicious userId values like admin/../public could access unintended document paths if not properly validated.

Firestore-Specific Detection

Detecting Firestore injection requires both static code analysis and dynamic runtime testing. middleBrick's Firestore-specific scanning examines your API endpoints for vulnerable query construction patterns.

Key detection patterns include:

  • Dynamic field access: Code that uses user input to determine collection names, field names, or operators
  • Unvalidated array operations: Queries using array-contains, array-contains-any, or in with user-controlled arrays
  • Path traversal: Document references constructed from unvalidated user input
  • Wildcard queries: Use of is-nan, is-null, or != with dynamic values

middleBrick tests these patterns by sending crafted payloads to your Firestore-backed API endpoints:

POST /api/search HTTP/1.1
Content-Type: application/json

{
  "field": "role",
  "operator": "==",
  "value": "admin" // Test for privilege escalation
}

The scanner also checks for Firestore-specific anti-patterns like:

// Dangerous: dynamic collection names
const collectionName = req.query.collection;
const query = firestore.collection(collectionName)
  .where('status', '==', 'active');

middleBrick's detection engine maps findings to OWASP API Security Top 10 categories, specifically A1: Broken Object Level Authorization and A3: Broken Object Property Level Authorization.

For comprehensive testing, middleBrick provides a security risk score (0-100) with detailed findings including:

  • Query construction patterns found
  • Input validation gaps identified
  • Authorization bypass attempts detected
  • Compliance mapping to standards like PCI-DSS and SOC2

Firestore-Specific Remediation

Secure Firestore implementations use strict input validation and avoid dynamic query construction. Here are Firestore-specific remediation patterns:

1. Whitelist field names and operators:

const VALID_FIELDS = ['email', 'status', 'createdAt'];
const VALID_OPERATORS = ['==', '>', '<', '>=', '<='];

function createSafeQuery(field, operator, value) {
  if (!VALID_FIELDS.includes(field)) {
    throw new Error('Invalid field');
  }
  if (!VALID_OPERATORS.includes(operator)) {
    throw new Error('Invalid operator');
  }
  
  return firestore.collection('users')
    .where(field, operator, value);
}

2. Use parameterized queries for array operations:

// Safe: explicit array validation
function searchByTags(userId, tags) {
  if (!Array.isArray(tags) || tags.length === 0) {
    throw new Error('Invalid tags');
  }
  
  // Validate each tag
  const validTags = tags.filter(tag => typeof tag === 'string' && tag.length <= 50);
  if (validTags.length !== tags.length) {
    throw new Error('Invalid tag format');
  }
  
  return firestore.collection('users')
    .where('userId', '==', userId)
    .where('tags', 'array-contains-any', validTags);
}

3. Secure document path construction:

function getValidUserId(userId) {
  // Validate UUID format or other expected pattern
  const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
  if (!uuidRegex.test(userId)) {
    throw new Error('Invalid user ID format');
  }
  return userId;
}

// Safe path construction
const userId = getValidUserId(req.params.userId);
const docRef = firestore.doc(`users/${userId}/settings/profile`);

4. Implement query depth limits:

function safeCollectionQuery(collectionName, filters) {
  const MAX_DEPTH = 3;
  const parts = collectionName.split('/');
  
  if (parts.length > MAX_DEPTH) {
    throw new Error('Query depth exceeds maximum');
  }
  
  // Only allow specific collections
  const allowedCollections = ['users', 'products', 'orders'];
  if (!allowedCollections.includes(parts[0])) {
    throw new Error('Invalid collection');
  }
  
  // Build query with validated filters
  let query = firestore.collection(collectionName);
  
  Object.entries(filters).forEach(([field, value]) => {
    if (VALID_FIELDS.includes(field)) {
      query = query.where(field, '==', value);
    }
  });
  
  return query;
}

5. Use Firestore security rules as defense-in-depth:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read, write: if request.auth.uid == userId;
      
      match /settings/{setting} {
        allow read, write: if request.auth.uid == userId;
      }
    }
    
    match /public/{document=**} {
      allow read: if true;
      allow write: if false;
    }
  }
}

For continuous security, middleBrick's Pro plan includes automated scanning that can be integrated into your CI/CD pipeline. Set up GitHub Actions to scan your Firestore-backed APIs before deployment:

name: Security Scan
on: [pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run middleBrick Scan
        run: |
          npx middlebrick scan https://api.yourdomain.com
        env:
          MIDDLEBRICK_API_KEY: ${{ secrets.MIDDLEBRICK_API_KEY }}
      - name: Fail on High Risk
        run: |
          # Check if scan found critical issues
          if [ $(grep -c "CRITICAL" security-report.json) -gt 0 ]; then
            exit 1
          fi

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Can Firestore's security rules prevent SQL injection attacks?
Firestore security rules provide defense-in-depth but don't prevent injection at the application level. Rules validate data access patterns but can't stop malicious query construction before it reaches the database. You need both secure application code AND proper security rules.
Does Firestore's query engine automatically sanitize input to prevent injection?
No, Firestore doesn't automatically sanitize input. While it prevents some classic SQL injection patterns due to its document model, you're still responsible for validating field names, operators, and array inputs. Malicious queries can still manipulate result sets and bypass authorization.