HIGH header injectionfeathersjsfirestore

Header Injection in Feathersjs with Firestore

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

Header Injection in a Feathersjs service that uses Firestore occurs when untrusted input from an HTTP request is directly used to set or modify HTTP headers before those headers are sent to Firestore or returned to the client. Feathersjs is a flexible framework that allows you to define hooks and services; if a hook dynamically sets headers such as x-goog-header or custom metadata based on request parameters, and those values are not strictly validated, an attacker can inject arbitrary headers.

Firestore, accessed via the Firebase Admin SDK or REST API, requires careful handling of metadata and headers. When Feathersjs passes user-controlled data into header construction logic, it can lead to response splitting, header smuggling, or leakage of internal configuration details. For example, an attacker might supply a newline character in a query parameter to inject additional headers, which may be ignored by Feathersjs but could be misinterpreted by an intermediary proxy or the Firestore client library if the request is forwarded improperly.

Consider a Feathersjs hook that adds a custom trace ID to the outgoing request to Firestore based on a query parameter:

app.service('logs').hooks({
  before: {
    create: [async context => {
      const traceId = context.params.query.traceId;
      if (traceId) {
        // Unsafe: directly using user input as a header value
        context.params.headers['x-custom-trace'] = traceId;
      }
      return context;
    }]
  }
});

If traceId contains newline sequences (e.g., value\r\nX-Injected: malicious), this can lead to header injection. Another scenario involves using request headers to influence Firestore document reads without validation:

app.service('data').hooks({
  before: {
    find: [async context => {
      const overrideLimit = context.params.headers['x-limit'];
      if (overrideLimit) {
        // Unsafe: header injection risk if header is attacker-controlled
        context.params.query.limit = parseInt(overrideLimit, 10);
      }
      return context;
    }]
  }
});

In this case, an attacker can manipulate the x-limit header to change pagination behavior, potentially leading to data exposure or denial of service. Because Firestore queries are constructed in memory, injected headers may not directly affect Firestore itself but can alter the request flow in Feathersjs, leading to incorrect query parameters or unexpected behavior.

The risk is compounded when combined with Firestore’s metadata handling. If Feathersjs uses user input to set Firestore document metadata or custom headers in REST calls, injection can bypass intended access controls. For instance, injecting a x-goog-user-project header could shift billing context, or injecting malformed headers might cause the Firestore client to misinterpret the request. Proper validation, strict allowlists, and avoiding the direct use of untrusted input for headers are essential to mitigate these risks in the Feathersjs and Firestore integration.

Firestore-Specific Remediation in Feathersjs — concrete code fixes

To remediate Header Injection in Feathersjs when working with Firestore, you must ensure that any user-controlled data never directly influences HTTP headers or Firestore request parameters. Use strict allowlists, explicit type checks, and avoid passing raw request inputs into headers or query construction.

First, validate and sanitize any input that might be used for headers. If a trace ID is required, generate it server-side instead of trusting the client:

const { v4: uuidv4 } = require('uuid');

app.service('logs').hooks({
  before: {
    create: [async context => {
      // Safe: generate server-side trace ID
      context.params.headers['x-custom-trace'] = uuidv4();
      return context;
    }]
  }
});

If you must accept a client-provided value, enforce a strict pattern and reject any input containing newline characters or control characters:

function isValidTraceId(value) {
  return typeof value === 'string' && /^[a-zA-Z0-9_-]{1,64}$/.test(value);
}

app.service('logs').hooks({
  before: {
    create: [async context => {
      const traceId = context.params.query.traceId;
      if (traceId && isValidTraceId(traceId)) {
        context.params.headers['x-custom-trace'] = traceId;
      }
      return context;
    }]
  }
});

For header-based overrides such as x-limit, avoid using headers entirely and prefer query parameters with validation:

app.service('data').hooks({
  before: {
    find: [async context => {
      const limit = context.params.query.limit;
      const parsedLimit = parseInt(limit, 10);
      if (Number.isInteger(parsedLimit) && parsedLimit > 0 && parsedLimit <= 100) {
        context.params.query.limit = parsedLimit;
      } else {
        delete context.params.query.limit;
      }
      return context;
    }]
  }
});

When interacting with Firestore, ensure that any metadata or custom headers are set using predefined, non-user-influenced values. For example, if you need to specify a project ID for billing, hardcode it or retrieve it from environment variables:

const admin = require('firebase-admin');
admin.initializeApp({
  projectId: process.env.FIREBASE_PROJECT_ID
});

// In a hook, avoid setting x-goog-user-project from user input
app.service('records').hooks({
  before: {
    find: [async context => {
      // Safe: use server-side configuration
      context.params.headers['x-goog-user-project'] = process.env.GCP_PROJECT_BILLING;
      return context;
    }]
  }
});

Finally, use Feathersjs middleware to sanitize all incoming headers before they reach services. This prevents accidental propagation of injected headers to Firestore calls:

app.hooks({
  before: {
    all: [async context => {
      const cleanHeaders = {};
      const allowedHeaders = ['authorization', 'content-type'];
      for (const [key, value] of Object.entries(context.headers)) {
        if (allowedHeaders.includes(key.toLowerCase())) {
          cleanHeaders[key] = value;
        }
      }
      context.headers = cleanHeaders;
      return context;
    }]
  }
});

By combining input validation, server-side generation, and strict header whitelisting, you eliminate the risk of Header Injection while maintaining safe integration with Firestore through Feathersjs.

Frequently Asked Questions

Can header injection bypass Firestore security rules?
Header injection in Feathersjs does not directly bypass Firestore security rules, but it can alter request parameters or metadata in ways that lead to incorrect query execution, potentially exposing data that should be restricted.
Does middleBrick detect header injection risks in API scans?
middleBrick scans unauthenticated attack surfaces and includes input validation and property authorization checks that can identify header manipulation risks; findings include severity, real CVE references, and remediation guidance.