HIGH use after freefeathersjsapi keys

Use After Free in Feathersjs with Api Keys

Use After Free in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability

A Use After Free condition occurs in a Feathersjs service when a resource (e.g., a data object or hook context) is deallocated or made unavailable while an Api Key–bound request still holds a reference to it, and subsequent operations attempt to use that stale reference. In Feathers, services often retain references to records, hooks, or transport contexts across asynchronous boundaries; if the underlying data structure is reused, mutated, or explicitly released (e.g., via pool return, object invalidation, or framework-level cleanup) while an Api Key validation or authorization step is still in flight, reads or writes can act on freed memory or inconsistent state.

When Api Keys are used for authentication, the key is typically validated early in the request lifecycle—often in a global hook or custom authentication hook—and the associated identity/permissions are attached to the request. If the service implementation or a hook later operates on a shared object that has been freed or reassigned (for example, a cached record that is evicted, a reused event payload, or an improperly scoped variable in an async chain), the request may continue using that invalid memory. This can lead to information disclosure, unexpected mutations, or crashes, depending on what the freed reference points to when accessed.

The combination of Feathersjs’s hook-based architecture and Api Key–driven identity creates specific conditions that can expose Use After Free:

  • Shared mutable state across hooks: If a hook mutates or releases an object that is later accessed by downstream hooks or the service method, and an Api Key binding identifies the request but does not guarantee isolation of the object lifecycle, the later access may operate on freed data.
  • Asynchronous reuse of records: Feathers services often return promises that resolve to records. If a record is released or overwritten in a cache/pool while the promise chain is still executing under the context of an Api Key–identified request, a Use After Free can occur when the chain tries to read or modify the record.
  • Transport or connection objects tied to Api Key sessions: In scenarios where transports (e.g., sockets or REST requests) keep references to request-scoped data for streaming or callbacks, freeing those objects before all callbacks complete while an Api Key is still logically tied to the request can cause use-after-free behavior at the application layer.

Real-world analogs in other ecosystems (such as use-after-free advisories tied to improper memory management in native extensions) map conceptually to Feathers when lifecycle ownership is not carefully isolated between authentication (Api Key validation) and resource handling. Even though Feathers runs in a managed runtime, incorrect handling of object lifetimes in hooks and services can produce security-relevant inconsistencies detectable by middleBrick’s checks for unsafe consumption and property authorization.

Api Keys-Specific Remediation in Feathersjs — concrete code fixes

Remediation focuses on ensuring that objects referenced during Api Key–bound authorization remain valid and isolated for the duration of the request. Use immutable copies for records exposed to hooks, enforce strict scoping, and avoid holding references to shared or pooled resources across asynchronous gaps.

Example 1: Isolate records with immutable copies after Api Key validation

After authenticating via an Api Key hook, create a deep copy of any record before passing it through hooks or returning it. This prevents later mutations or frees from affecting the in-flight request.

// src/hooks/authenticate-api-key.js
const crypto = require('node:crypto');

module.exports = function authenticateApiKey(options = {}) {
  return async context => {
    const { app, method, params } = context;
    const providedKey = params.query?.apiKey || context.headers['x-api-key'];

    if (!providedKey) {
      throw new Error('API key is required');
    }

    // Validate key against a store (e.g., database, KV)
    const keyRecord = await app.service('apiKeys').get(providedKey, { query: { $select: ['id', 'scope', 'hash'] } });
    if (!keyRecord || !keyRecord.hash) {
      throw new Error('Invalid API key');
    }

    // Ensure the key is bound to the requested scope
    if (!keyRecord.scope || !keyRecord.scope.includes(method)) {
      throw new Error('Insufficient scope for API key');
    }

    // Create an immutable copy to avoid use-after-free across hooks
    context.apiKey = Object.freeze({ id: keyRecord.id, scope: [...keyRecord.scope] });
    // Optionally redact sensitive fields from params
    context.params = { ...context.params, apiKey: undefined };
    return context;
  };
};

Example 2: Safe service method with scoped data and no cross-request references

Ensure service methods do not reuse objects that may be freed by hooks or external caches. Use local variables and avoid attaching request-bound data to long-lived stores.

// src/services/users/users.class.js
const { Service } = require('feathers');

class UsersService extends Service {
  async find(params) {
    const { apiKey } = params;
    // Local, scoped data; do not hold references to params beyond this call
    const users = await super.find({ query: { userId: apiKey?.id } });
    // Return a plain, isolated snapshot
    return users.map(u => ({ ...u, password: undefined }));
  }

  async get(id, params) {
    const { apiKey } = params;
    // Validate scope locally; do not rely on external mutable state
    const record = await super.get(id, { query: { userId: apiKey?.id } });
    // Return a fresh copy to avoid use-after-free in downstream hooks
    return { ...record, token: undefined };
  }
}

module.exports = function initAppServices(app) {
  const options = { Model: app.get('UserModel'), paginate: { default: 25, max: 50 } };
  app.use('/users', new UsersService(options));
};

Example 3: Hook-level isolation to prevent dangling references

In before hooks, avoid mutating shared context objects that could be freed later. Instead, produce new objects for downstream use.

// src/hooks/ensure-record-isolation.js
module.exports = function ensureRecordIsolation(options = {}) {
  return async context => {
    const { result } = context;
    if (result && typeof result === 'object' && !Array.isArray(result)) {
      // Replace with a frozen copy to prevent later frees from affecting this request
      context.result = Object.freeze({ ...result });
    } else if (Array.isArray(result)) {
      context.result = result.map(item => Object.freeze({ ...item }));
    }
    return context;
  };
};

Operational guidance

  • Use middleware or hooks to validate Api Keys early and freeze or copy sensitive outputs before further processing.
  • Avoid caching or pooling record objects that are directly exposed to hooks; prefer immutable snapshots.
  • Leverage middleBrick’s checks for unsafe consumption and property authorization to detect patterns that may lead to Use After Free in Feathersjs setups.

Frequently Asked Questions

How does middleBrick detect Use After Free risks in Feathersjs with Api Keys?
middleBrick runs parallel security checks including unsafe consumption and property authorization. It correlates Api Key validation points with object lifecycle findings to highlight patterns where records may be accessed after deallocation.
Can the GitHub Action prevent builds when Api Key–related Use After Free risks are detected?
Yes. The GitHub Action can be configured with a security score threshold; if the scan uncovers high-severity issues such as unsafe consumption or authorization gaps tied to Api Keys, the build can fail to prevent deployment of vulnerable configurations.