HIGH nosql injectionfeathersjsbasic auth

Nosql Injection in Feathersjs with Basic Auth

Nosql Injection in Feathersjs with Basic Auth — how this specific combination creates or exposes the vulnerability

FeathersJS is a JavaScript framework for building REST and real-time APIs. By default, Feathers services accept query parameters directly from incoming requests. When a service uses a database adapter that translates query objects into database queries (for example, an adapter for MongoDB or NeDB), unsanitized user input can alter query logic. This is the core of NoSQL injection: attacker-controlled values change the structure or intent of a query.

Basic Auth in Feathers is typically implemented via an authentication hook that checks a username and password from the header. A common pattern is to use authentication with a service that stores user credentials. If the authentication hook runs before the service query is built, and the service later uses request parameters (e.g., query.userId or query._id) without validation, an authenticated context can still be abused if input validation is weak or absent.

The combination is risky when:

  • The service exposes query parameters that are used to build database queries.
  • Input is not validated or sanitized (e.g., userId is taken directly from req.query).
  • An operator mistakenly believes that Basic Auth alone limits what an authenticated user can query.

For example, consider a Feathers service for user profiles. An endpoint like GET /profiles?userId=5 might be implemented as:

app.service('profiles').find({
  query: {
    userId: req.query.userId
  }
});

If userId is not validated, an authenticated attacker could send userId[$ne]=null to enumerate all profiles, or userId[$gt]='' to perform a blind injection to infer data. In MongoDB, operators like $ne, $gt, $regex, and nested operators can change query semantics significantly. Even with Basic Auth, if the service does not enforce ownership checks (e.g., ensuring the requesting user can only access their own data), NoSQL injection can lead to horizontal privilege escalation.

Feathers does not execute arbitrary code via query objects, but it does pass them directly to the adapter. This means operators like $where (in MongoDB) or aggregation stages can be particularly dangerous if user input is interpolated. Real-world findings from scans have shown patterns similar to CVE-classic data exfiltration techniques where injection via query parameters exposed sensitive records. The risk is elevated when services rely on dynamic query building without schema validation or strict allowlists.

To determine whether a specific deployment is vulnerable, a scanner runs parallel checks that include input validation and authorization tests. It submits crafted query parameters while authenticated via Basic Auth and inspects whether the response set changes in unintended ways, indicating that query logic is being altered by attacker-controlled data.

Basic Auth-Specific Remediation in Feathersjs — concrete code fixes

Remediation focuses on strict input validation, explicit ownership checks, and avoiding direct passthrough of request parameters into database queries. Below is a secure Feathers authentication setup using Basic Auth, followed by a safe service implementation.

First, configure Basic Auth authentication. This example uses @feathersjs/authentication and @feathersjs/authentication-local:

const authentication = require('@feathersjs/authentication');
const local = require('@feaths/authentication-local');

app.configure(authentication({
  entity: 'user',
  service: 'users',
  secret: 'super-secret-jwt-or-encrypt-key-change-this',
  authStrategies: ['local']
}));

app.use('/authentication', authentication({
  service: app.service('authentication'),
  entity: 'user',
  header: 'Authorization',
  resolver: authentication.resolvers.headers(),
  userEntityId: 'user._id'
}).hooks({
  before: {
    create: [ authentication.hooks.authenticate('local') ]
  }
}));

Next, implement the users service with safe password handling. Note that the password field is excluded from results by default when using authentication helpers:

const { iff, isProvider } = require('feathers-hooks-common');
const { hashPassword, protect } = require('@feathersjs/authentication-local').hooks;

app.use('/users', createService({
  Model: UserModel,
  paginate: {
    default: 10,
    max: 50
  },
  hooks: {
    before: {
      create: [ hashPassword('password') ],
      update: [ iff(isProvider('external'), protect('password')) ],
      patch: [ iff(isProvider('external'), protect('password')) ]
    },
    after: {
      all: [ protect('password') ],
      find: [],
      get: [],
      create: [],
      update: [],
      patch: [],
      remove: []
    }
  }
}));

For profile services, enforce ownership and validate identifiers. Do not trust req.query.userId. Instead, derive the user ID from the authenticated context:

const { iff, isProvider, preventChanges } = require('feathers-hooks-common');

app.service('profiles').hooks({
  before: {
    find: [ context => {
      // Ensure the query only returns the authenticated user's profile
      context.params.query.userId = context.params.user._id;
      return context;
    }],
    get: [ context => {
      // Prevent lookup of other users' profiles
      if (context.id !== context.params.user._id) {
        throw new Error('Forbidden');
      }
      return context;
    }],
    create: [ preventChanges('external', 'userId') ],
    update: [ iff(isProvider('external'), preventChanges('external')) ],
    patch: [ iff(isProvider('external'), preventChanges('external')) ]
  },
  after: {
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  }
});

Additional measures:

  • Validate and sanitize all incoming query and body fields. Use libraries like joi or celebrate and apply hooks before the service logic.
  • Avoid dynamic operators in user-controlled input. If you must support advanced queries, implement a strict allowlist of permitted fields and operators.
  • Use the CLI (middlebrick scan <url>) and the GitHub Action to include runtime checks for authorization and input validation issues in your CI/CD pipeline.

Frequently Asked Questions

Can Basic Auth alone prevent NoSQL injection in Feathers services?
No. Basic Auth confirms identity but does not restrict what an authenticated user can query. Without input validation and explicit ownership checks, query parameters can still be manipulated to alter database operations.
How can I test my Feathers API for NoSQL injection using middleBrick?
Run the CLI command middlebrick scan <your-api-url>. The scanner performs authenticated and unauthenticated checks, including input validation and authorization tests, and reports findings mapped to frameworks such as OWASP API Top 10.