HIGH double freefeathersjsjwt tokens

Double Free in Feathersjs with Jwt Tokens

Double Free in Feathersjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Double free is a memory safety class of vulnerability that can manifest in native or extension code, but when it occurs in a FeathersJS application using JWT tokens it usually indicates misuse of objects that are freed or invalidated more than once during request handling. In the FeathersJS + JWT context, a double free can arise when token lifecycle management interacts with authentication hooks and service-side state in an unsafe way.

Consider a typical FeathersJS authentication flow where a JWT is verified and a user object is attached to the connection or request. If the application or an authentication hook frees or resets an internal data structure (for example, clearing a cached decoded payload or a session mapping) and then later code attempts another cleanup or finalization on the same structure, a double free condition can be triggered. This is especially risky when JWT handling code uses native addons, C++ bindings, or certain caching libraries that manage their own memory, and when FeathersJS hooks process errors or redirect flows without guarding against already-released resources.

An example pattern that can lead to a double free involves attaching decoded JWT data to the FeathersJS connection and then clearing it in an error handler while also having a hook or transport layer cleanup that runs subsequently:

// FeathersJS hook that attaches JWT payload and later clears it
const jwt = require('@feathersjs/authentication-jwt');

function attachJwtPayload(hook) {
  const { token } = hook.params.query;
  if (token) {
    const decoded = jwt.decode(token, { complete: false });
    // Attach payload for downstream services
    hook.app.set('currentUserPayload', decoded);
    hook.params.userPayload = decoded;
  }
  return Promise.resolve(hook);
}

// Error cleanup hook that may run after authentication failure
function cleanupPayload(hook) {
  const payload = hook.app.get('currentUserPayload');
  if (payload) {
    // Simulated unsafe free: clearing and nulling an object that native code or extensions may still reference
    hook.app.set('currentUserPayload', null);
    // If another hook or transport layer also tries to release or mutate the same reference,
    // and the runtime uses native memory management, a double free may occur.
  }
  return Promise.resolve(hook);
}

In this pattern, if hook.app.set or an underlying extension performs a release on null/clear and another cleanup routine (for example from JWT cookie parsing or connection teardown) also tries to release the same cached object, the runtime may encounter a double free. This is more likely when using native modules for JWT decoding or when integrating with systems that manage their own reference counts. The vulnerability is not in JWT cryptography itself but in how FeathersJS application state and native integrations coordinate lifecycle operations on shared objects.

Double free scenarios can also surface during token refresh or logout flows where JWT blacklisting or session maps are manipulated. If FeathersJS services or hooks mutate these structures concurrently or without proper idempotency guards, repeated frees may happen. Because FeathersJS is a JavaScript runtime, the practical impact often surfaces through instability or crashes in native dependencies rather than direct memory corruption within JavaScript, but the root cause remains the same: multiple deallocation attempts on the same resource.

Jwt Tokens-Specific Remediation in Feathersjs — concrete code fixes

Remediation focuses on ensuring JWT payload attachment and cleanup are idempotent, avoiding shared mutable state where possible, and guarding against repeated frees or cleanups. Below are concrete FeathersJS code examples that demonstrate safe patterns.

Idempotent attachment and cleanup

Ensure that attaching and clearing JWT payloads checks current state and avoids repeated operations on the same object:

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

function safeAttachJwtPayload(hook) {
  const { token } = hook.params.query || {};
  if (token) {
    const decoded = jwt.decode(token, { complete: false });
    // Only attach if not already present to prevent double processing
    if (!hook.app.get('currentUserPayload')) {
      hook.app.set('currentUserPayload', decoded);
    }
    hook.params.userPayload = decoded;
  }
  return Promise.resolve(hook);
}

function safeCleanupPayload(hook) {
  const payload = hook.app.get('currentUserPayload');
  if (payload) {
    // Idempotent clear: set to null only if currently set
    hook.app.set('currentUserPayload', null);
    // Avoid further cleanup on the same reference in this request lifecycle
    hook.params.userPayload = null;
  }
  return Promise.resolve(hook);
}

Separate lifecycle phases and avoid shared mutable state

Use request-scoped storage instead of app-level mutable state when possible. This reduces cross-request contamination and the chance of double-free-like behavior when hooks run in varied orders:

function requestScopedJwtAttach(hook) {
  const { token } = hook.params.query || {};
  if (token) {
    const decoded = jwt.decode(token, { complete: false });
    // Store on the hook itself (request-local) rather than app-wide
    hook.feathersJwtPayload = decoded;
    hook.params.userPayload = decoded;
  }
  return Promise.resolve(hook);
}

function requestScopedJwtCleanup(hook) {
  if (hook.feathersJwtPayload) {
    // No app-wide mutation; cleanup is confined to this request
    hook.feathersJwtPayload = null;
    hook.params.userPayload = null;
  }
  return Promise.resolve(hook);
}

Guarding against repeated error-hook execution

Ensure error handlers are not applied multiple times per request by using flags or framework conventions. FeathersJS hooks can be invoked in multiple phases (error, after); make cleanup phase-aware:

function guardedCleanup(hook) {
  // Skip if already cleaned up in an earlier hook phase
  if (hook.params._payloadCleaned) {
    return Promise.resolve(hook);
  }
  const payload = hook.app.get('currentUserPayload');
  if (payload) {
    hook.app.set('currentUserPayload', null);
    hook.params._payloadCleaned = true; // mark as cleaned
  }
  return Promise.resolve(hook);
}

Dependency and native integration review

If using native modules or npm packages that manage their own memory, audit their documentation for release patterns and ensure FeathersJS hooks do not trigger duplicate lifecycle actions (e.g., explicit free/delete calls alongside garbage collection). Prefer pure-JWT libraries when possible and avoid mixing multiple token decoding strategies within the same request lifecycle.

By normalizing attachment and cleanup to be idempotent, scoping state to the request lifecycle, and guarding against repeated execution, the double free exposure window for JWT handling in FeathersJS is effectively closed while preserving correct authentication behavior.

Frequently Asked Questions

Can a double free in FeathersJS with JWT tokens lead to remote code execution?
In FeathersJS, a double free is typically limited to instability or crashes in native dependencies; it does not directly allow remote code execution through JWT handling alone. However, it can indicate unsafe memory management patterns that may be part of a broader class of vulnerabilities.
Does middleBrick detect double free conditions in API scans?
middleBrick focuses on API security risk scoring and findings such as authentication issues, injection vectors, and data exposure. It does not detect memory safety issues like double free; remediation requires code review and safe lifecycle management in the application.