HIGH double freefeathersjsbasic auth

Double Free in Feathersjs with Basic Auth

Double Free in Feathersjs with Basic Auth — how this specific combination creates or exposes the vulnerability

A Double Free vulnerability in a FeathersJS application using Basic Authentication arises when an authenticated request triggers repeated or incorrect deallocation of the same memory/resource on the server side, often through misuse of authentication state or hooks. In Feathers, authentication plugins typically attach a user object to the connection context (e.g., context.params). If the server code or a hook erroneously processes authentication cleanup multiple times—such as calling a user logout or session invalidation routine more than once per request—it can corrupt memory or lead to undefined behavior when combined with how Feathers manages service methods and hooks.

Consider a Feathers service where Basic Auth is validated via a custom hook that attaches a normalized user object to params. If two hooks (e.g., an authentication hook and an authorization or before hook) both attempt to modify or clear the same context property (like params.account) without idempotency guards, the runtime may attempt to free the same underlying object twice, resulting in a Double Free. This is more likely when developers implement manual session invalidation or token blacklisting in multiple hook layers without ensuring each step is safe to run multiple times.

The risk is compounded when error handling in hooks is inconsistent. For example, if a hook throws an error after partially modifying context, Feathers may retry or re-enter certain lifecycle steps, causing another cleanup attempt on an already-freed resource. Because Feathers relies on a chain of hooks, the order and composition of authentication and service hooks directly affect whether a Double Free can occur. An attacker could exploit this by sending crafted requests that force specific hook sequences, potentially leading to crashes or erratic server behavior that can be probed via unauthenticated endpoints tested by a scanner like middleBrick, which checks for authentication misconfigurations and BOLA/IDOR issues in under 15 seconds.

Real-world patterns that can contribute include defining multiple before hooks that each call a user-cleanup utility, or mixing custom authentication logic with Feathers’ built-in authentication strategies without ensuring idempotency. For instance, a hook that calls hook.app.service('sessions').remove(token) without checking whether the session was already removed can intersect with another hook doing the same. While Feathers does not manage low-level memory directly, the JavaScript runtime (e.g., Node.js garbage collector) can exhibit instability when objects are freed and referenced in inconsistent states, which may cascade into service instability.

To detect such issues, a security scan using middleBrick’s authentication and BOLA/IDOR checks examines how authentication state is handled across hooks and services, particularly looking for unsafe repeated modifications of context properties. The scanner’s inventory management and unsafe consumption checks can surface risky patterns where hooks lack proper guards. Remediation focuses on making authentication lifecycle operations idempotent and ensuring hooks do not assume exclusive ownership of context mutation, which aligns with secure coding practices for FeathersJS services.

Basic Auth-Specific Remediation in Feathersjs — concrete code fixes

Remediation for Double Free risks in FeathersJS with Basic Auth centers on ensuring authentication hooks are idempotent and avoid repeated mutation of the same context properties. Use a guard flag or state check before modifying or clearing authentication-related context. Below are concrete code examples demonstrating secure patterns.

Example 1: Idempotent Authentication Hook

Ensure your authentication hook sets user data only once and skips if already present:

// src/hooks/authenticate.js
module.exports = function authenticate(options = {}) {
  return async context => {
    const { params } = context;
    // Guard: skip if user already attached
    if (params.user) {
      return context;
    }
    const authHeader = params.headers.authorization || '';
    const match = authHeader.match(/^Basic\s+(.*)$/i);
    if (!match) {
      throw new Error('Unauthenticated');
    }
    const decoded = Buffer.from(match[1], 'base64').toString('utf-8');
    const [username, password] = decoded.split(':');
    // Validate credentials securely (use a service lookup)
    const account = await validateBasicCredentials(username, password);
    if (!account) {
      throw new Error('Invalid credentials');
    }
    // Attach user once; do not overwrite if already set
    params.user = { id: account.id, username: account.username };
    return context;
  };
};

async function validateBasicCredentials(username, password) {
  // Replace with real user lookup, e.g., using a Feathers service
  if (username === 'admin' && password === 'securepassword') {
    return { id: 1, username: 'admin' };
  }
  return null;
}

Example 2: Safe Hook Order and Cleanup Guard

Avoid multiple hooks attempting to clean up the same resource by using a shared flag:

// src/hooks/cleanup-user.js
const cleanupKey = Symbol('cleanupUser');
module.exports = function cleanupUser(options = {}) {
  return async context => {
    // Ensure cleanup runs at most once per context
    if (context[cleanupKey]) {
      return context;
    }
    context[cleanupKey] = true;
    if (context.params.user && context.params.user._cleanup) {
      // Perform safe teardown only if needed; do not double-free
      context.params.user._cleanup();
    }
    return context;
  };
};

// In src/hooks/index.js — order matters
const authenticate = require('./authenticate');
const cleanupUser = require('./cleanup-user');

module.exports = {
  before: {
    all: [authenticate, cleanupUser], // authenticate first, then optional cleanup guard
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  }
};

Example 3: Using Feathers Authentication with Secure Defaults

Leverage @feathersjs/authentication and @feathersjs/authentication-local where possible to avoid custom hook pitfalls:

// src/authentication.js
const authentication = require('@feathersjs/authentication');
const local = require('@feathersjs/authentication-local');

const auth = authentication({
  entity: 'user',
  service: 'users',
  secret: process.env.APP_SECRET,
  strategies: ['local']
});

const localStrategy = local({
  usernameField: 'username',
  passwordField: 'password',
  session: false // keep stateless for APIs; set true only if using sessions
});

module.exports = {
  auth,
  localStrategy
};

// src/hooks/index.js
const { auth, localStrategy } = require('../authentication');

module.exports = {
  before: {
    all: [auth.hooks.authenticate()], // Uses built-in strategy, reducing custom hook risks
    // other before hooks
  }
};

These examples emphasize validating credentials once, attaching user data to params only when absent, and avoiding duplicate cleanup routines. They complement middleBrick’s authentication and BOLA/IDOR checks, which can verify that authentication hooks do not expose unsafe state handling. For teams using the Pro plan, continuous monitoring can detect regressions in hook behavior across deployments, and the GitHub Action can fail builds if authentication-related risk patterns are found.

Frequently Asked Questions

Can a Double Free in Feathers with Basic Auth be detected by unauthenticated scans?
Yes, a scanner like middleBrick can identify risky authentication hook patterns and missing idempotency guards during unauthenticated scans by analyzing hook compositions and context mutation sequences, even when endpoints do not require credentials.
Does fixing Double Free in Feathers require changes to the OpenAPI spec?
Not necessarily. The fix is primarily in hook and service implementation to ensure idempotent context handling. However, if authentication behavior changes the shape of context or responses, update the OpenAPI spec accordingly so runtime checks stay aligned with spec expectations.