HIGH injection flawsexpressfirestore

Injection Flaws in Express with Firestore

Injection Flaws in Express with Firestore — how this specific combination creates or exposes the vulnerability

Injection flaws occur when an attacker can insert or "inject" hostile data into a command or query. In an Express application that uses Cloud Firestore, the risk typically arises when request parameters (query strings, path parameters, or body fields) are directly mapped into Firestore read or write operations without proper validation, sanitization, or use of safe abstractions.

Firestore itself is a managed NoSQL service and does not use traditional SQL, so classic SQL injection is not possible. However, injection-style problems can manifest in other ways: for example, an attacker may manipulate document IDs, field names, or values to cause the server to read or write unintended data, or to trigger unexpected behavior in security rules. When Firestore operations are built from unchecked user input, this can lead to Insecure Direct Object References (IDOR), excessive data exposure, or bypass of intended access controls.

Consider an Express route that retrieves a user profile using a document ID taken directly from the request parameters:

app.get('/profile/:docId', async (req, res) => {
  const doc = await db.collection('profiles').doc(req.params.docId).get();
  res.json(doc.data());
});

If the server does not verify that the authenticated user is allowed to access the supplied docId, a BOLA/IDOR issue exists. An attacker can enumerate or access other users' profiles by guessing or iterating document IDs. Additionally, if field names or map keys are derived from user input without strict allowlisting, an attacker may supply crafted input that writes to or reads sensitive fields, effectively achieving injection-like behavior through structure manipulation.

Another scenario involves constructing Firestore queries using concatenated or unsanitized input:

app.get('/search', async (req, res) => {
  const q = req.query.q;
  const snapshot = await db.collection('posts').where('title', '==', q).get();
  res.json(snapshot.docs.map(d => d.data()));
});

If q is not validated, an attacker may supply values intended to change query semantics or explore data structures. While Firestore does not interpret this as executable code, the query may expose more data than intended or be used in combination with other vulnerabilities to achieve privilege escalation or data exfiltration.

Insecure deserialization is less relevant for Firestore than for relational databases, but unsafe consumption of incoming data when writing to the database can still lead to injection-style issues. For example, blindly passing request body contents into a set or update operation may allow an attacker to inject unexpected fields, including metadata or nested objects that violate application logic.

middleBrick scans such endpoints as part of its BOLA/IDOR and Property Authorization checks, detecting whether document access patterns rely on unvalidated input and whether authorization is enforced at the data layer. By correlating runtime behavior with OpenAPI specifications, the scanner can highlight endpoints where user-controlled data directly influences Firestore operations, flagging potential injection-style risks and providing remediation guidance.

Firestore-Specific Remediation in Express — concrete code fixes

Defending against injection-style issues with Firestore in Express centers on strict input validation, canonicalization, and applying the principle of least privilege. Always prefer using Firestore's built-in mechanisms — such as document ID validation rules and security rules — rather than trying to sanitize data manually in application code.

1. Validate and canonicalize document IDs. Do not trust raw IDs from the request. Use allowlists or deterministic derivation (e.g., mapping authenticated user IDs to document IDs) instead of passing client-supplied IDs directly into paths.

const uid = req.user.uid; // authenticated subject
const docId = uid; // canonical mapping, no user input used as ID
const doc = await db.collection('profiles').doc(docId).get();

2. Use Firestore security rules to enforce access control. Even when your Express code applies checks, rules provide a critical last line of defense. Ensure rules validate that the requesting user can only access documents they own or are permitted to view:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /profiles/{docId} {
      allow read, write: if request.auth != null && request.auth.uid == docId;
    }
  }
}

3. Avoid dynamic field names derived from user input. If you must write user-supplied values, use strict allowlists for field names and treat incoming maps as untrusted payloads:

const allowedFields = new Set(['displayName', 'photoURL']);
const updates = {};
for (const [key, value] of Object.entries(req.body)) {
  if (allowedFields.has(key)) {
    updates[key] = value;
  }
}
await db.collection('profiles').doc(uid).update(updates);

4. Use parameterized queries and avoid concatenating user input into query structures. For equality checks, explicitly bind values; for more complex queries, enforce server-side validation and consider using Firestore's indexing rules to constrain result sets:

const q = req.query.q;
if (typeof q !== 'string' || q.length > 100) {
  return res.status(400).send('invalid query');
}
const snapshot = await db.collection('posts').where('title', '==', q).get();

5. Apply defense in depth with middleware validation and sanitization libraries, but remember that these are complementary to — not replacements for — Firestore rules. Validate data types, lengths, and formats before using them in Firestore operations:

const { body, param } = require('express-validator');
app.post('/posts/:docId',
  param('docId').isLength({ max: 20 }).isAlphanumeric(),
  body('title').isLength({ max: 200 }),
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    await db.collection('posts').doc(req.params.docId).set({
      title: req.body.title,
      updatedAt: new Date(),
    });
    res.sendStatus(200);
  }
);

These practices reduce the likelihood of injection-style issues by ensuring that user input never directly dictates Firestore document paths, query structure, or write payloads. middleBrick’s Property Authorization and Input Validation checks can help verify that your endpoints follow these patterns and surface risky mappings before deployment.

Frequently Asked Questions

Can Firestore injection flaws lead to full account compromise?
Yes. If an attacker can manipulate document IDs or field names to access or modify data outside intended boundaries, they may be able to read sensitive information or make unauthorized writes. This can lead to privilege escalation or data exfiltration, especially when combined with misconfigured security rules.
Does middleBrick test for injection-like behavior with Firestore?
Yes. middleBrick runs BOLA/IDOR and Property Authorization checks, and maps findings to frameworks such as OWASP API Top 10. It analyzes both runtime behavior and OpenAPI/Swagger specs (with full $ref resolution) to detect endpoints where user-controlled input influences Firestore operations, providing prioritized findings and remediation guidance.