HIGH injection flawsfeathersjsfirestore

Injection Flaws in Feathersjs with Firestore

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

FeathersJS is a framework for creating JavaScript and TypeScript APIs with a service-based architecture. When used with Google Cloud Firestore as the data store, injection flaws often arise from improperly constructed queries, especially where user input is directly interpolated into query constraints or used to dynamically reference document paths. Unlike SQL, Firestore does not support parameterized prepared statements in the same way; instead, correctness depends on strict input validation and disciplined query construction.

An injection flaw occurs when untrusted data influences query structure in a way that allows an attacker to read or modify data beyond intended permissions. For example, concatenating user-supplied strings into a document path to fetch or update a Firestore document can enable horizontal privilege escalation (BOLA/IDOR) or vertical privilege escalation if combined with flawed service logic. Consider a FeathersJS service defined as app.use('messages', new FirestoreService({ … })); if the service relies on route parameters such as :userId to build a Firestore path without strict validation, an attacker can substitute another user’s ID to access messages they should not see.

Firestore’s query language also supports array-contains and map lookups, which can be abused if input is not sanitized. An attacker might supply an array payload that causes a query to return more documents than intended, effectively bypassing intended access boundaries. In addition, deeply nested field paths in filters can be manipulated if keys are derived from user input, leading to unexpected document reads or writes. These patterns are common in services that dynamically construct field names or use input to navigate document hierarchies.

Another variant involves injection at the integration layer between FeathersJS and Firestore transactions or batch writes. If user data is used to determine which documents to read or write without preconditions or strict allow-list validation, an attacker can force writes to arbitrary documents or execute reads across collections. This is especially risky when the service exposes generic CRUD endpoints that accept document IDs or collection names as parameters. The absence of server-side schema validation in Firestore amplifies the impact: there is no native type enforcement at the database layer to prevent malformed or maliciously crafted queries from succeeding.

Because FeathersJS often serves as an abstraction layer, developers may assume the framework provides implicit protection. However, without explicit validation of IDs, paths, and query parameters, the combination of FeathersJS services and Firestore can inadvertently expose an unauthenticated or low-privilege attack surface. Regular security scans using tools that test injection vectors across the unauthenticated attack surface can help detect these issues early, highlighting risky endpoints before they are exploited in production.

Firestore-Specific Remediation in Feathersjs — concrete code fixes

Remediation centers on strict input validation, canonical path construction, and avoiding dynamic query assembly based on untrusted data. Always treat IDs and path segments as untrusted and validate them against an allow-list pattern before using them in Firestore operations. Below are concrete examples that demonstrate secure patterns for common FeathersJS + Firestore scenarios.

Secure document retrieval with ID validation

Instead of directly using route parameters to build document paths, validate the ID and reference the document explicitly:

const { Firestore } = require('@google-cloud/firestore');
const firestore = new Firestore();

// Validate ID format (alphanumeric and safe chars only)
function isValidId(id) {
  return /^[a-zA-Z0-9_-]{1,100}$/.test(id);
}

app.service('messages').hooks({
  before: {
    get: [context => {
      if (!isValidId(context.id)) {
        throw new Error('Invalid message ID');
      }
      // Use a canonical path; do not concatenate user input
      context.params.firestoreRef = firestore.doc(`users/${context.params.userId}/messages/${context.id}`);
      return context;
    }]
  }
});

Query with explicit field references and allow-listed filters

Avoid dynamic field names. Use static field references and validate any filter values:

app.service('messages').hooks({
  before: {
    find: [context => {
      const { status } = context.query;
      // Allow-list valid status values
      if (status && !['active', 'archived', 'deleted'].includes(status)) {
        delete context.query.status;
      }
      // Build query with explicit field path
      let query = firestore.collection('messages').where('status', '==', status || 'active');
      // If additional safe filters are needed, add them explicitly
      if (context.params.userId) {
        query = query.where('userId', '==', context.params.userId);
      }
      context.params.firestoreQuery = query;
      return context;
    }]
  }
});

Transaction with preconditions and static document paths

When using transactions, ensure document paths are constructed from trusted constants or validated inputs, and include existence preconditions:

async function updateUserScore(userId, delta) {
  if (!isValidId(userId)) {
    throw new Error('Invalid user ID');
  }
  const userRef = firestore.doc(`users/${userId}`);
  const scoreRef = firestore.doc(`users/${userId}/scores/current`);

  await firestore.runTransaction(async transaction => {
    const userDoc = await transaction.get(userRef);
    if (!userDoc.exists) {
      throw new Error('User does not exist');
    }
    transaction.set(scoreRef, { value: delta, updatedAt: new Date() }, { merge: true });
  });
}

By combining rigorous input validation, static path composition, and explicit query constraints, you reduce the risk of injection-related BOLA/IDOR and privilege escalation when using FeathersJS with Firestore. These practices align with the principle of least privilege and help ensure that the unauthenticated attack surface tested by middleBrick remains as limited as possible in production deployments.

Frequently Asked Questions

How can I test if my FeathersJS + Firestore API is vulnerable to injection flaws?
Use a black-box scanner that tests unauthenticated endpoints and query manipulation. Submit your API URL to a service that runs injection-specific test probes against Firestore endpoints to detect improper ID handling or dynamic query construction.
Does enabling Firestore security rules fully prevent injection vulnerabilities in FeathersJS?
Security rules provide important boundaries, but they do not replace input validation in application code. Rules can be misconfigured or bypassed if client-supplied data directly influences document paths or query structures. Validate and sanitize inputs in FeathersJS services regardless of rule configuration.