HIGH time of check time of usefeathersjsapi keys

Time Of Check Time Of Use in Feathersjs with Api Keys

Time Of Check Time Of Use in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability

Time Of Check Time Of Use (TOCTOU) occurs when the outcome of a security decision depends on the timing between a check and the subsequent use of a resource. In FeathersJS applications that rely on API keys for authentication, TOCTOU can manifest when authorization checks are performed separately from the actual service operation, creating a window where state changes can invalidate earlier decisions.

Consider a FeathersJS service that first validates an API key via an authentication hook, then later performs a lookup to verify whether the associated client is permitted to access a specific resource. If the check and the use are not atomic, an attacker can change the underlying data between these steps. For example, an API key might initially be valid for organization A, but before the service uses the key to fetch organization-specific data, the key is reassigned to organization B. Because the check and use are separate, the service may operate on organization A’s data using credentials that now belong to organization B, effectively bypassing intended isolation.

In FeathersJS, hooks run as middleware and can introduce TOCTOU when they perform non-atomic validation patterns. A common pattern is to look up a key in one hook to attach metadata (e.g., organization ID) to the params object, then let a service handler assume that metadata is still accurate when it executes. If the key’s permissions or associations are mutable and can be changed by an authenticated actor or through another API endpoint, the handler may execute with stale assumptions. This is particularly risky when API keys are used for impersonation or tenant segregation, as the handler may unknowingly operate with elevated or incorrect context.

Real-world attack patterns mirror issues described in OWASP API Top 10 (2023), such as Broken Function Level Authorization (BFLA), where lack of proper context validation enables privilege escalation. For example, an attacker could call a mutation that updates key bindings in one request, then immediately invoke a data-fetching endpoint that relied on a prior key validation check. If the service does not re-validate context at the point of use, the attacker may access or modify data they should not reach.

Additionally, if the service exposes an unauthenticated endpoint that reports key metadata or status, an attacker can probe timing differences to infer when checks and uses are separated. Combined with the inherent concurrency model of Node.js runtimes in FeathersJS, this can amplify the impact of TOCTOU by making race conditions more likely. The framework does not provide built-in atomicity guarantees for hook-to-handler transitions, so developers must design checks and usage to be tightly coupled or re-validate critical decisions immediately before performing sensitive operations.

Using OpenAPI/Swagger analysis with full \$ref resolution can help identify whether authorization checks are duplicated across paths and whether definitions inadvertently encourage split validation logic. When scanning a FeathersJS API, pay attention to operations that accept API keys in headers but then rely on cached or previously attached claims without re-verifying them against the current request context.

Api Keys-Specific Remediation in Feathersjs — concrete code fixes

To mitigate TOCTOU in FeathersJS when using API keys, ensure that authorization decisions are made as close as possible to the point of use, and avoid relying on mutable state that can change between a check and a use. Prefer re-validating the key and its permissions within the same hook or service method, and avoid attaching long-lived assumptions to params that can be invalidated by other endpoints.

Below are concrete code examples demonstrating secure patterns.

Example 1: Atomic validation inside a service method

Instead of attaching key metadata in a prior hook and trusting it later, validate the key and fetch permissions within the service method itself. This removes the timing gap.

// services/items/service.js
const { Forbidden } = require('@feathersjs/errors');

module.exports = function (service) {
  return async context => {
    const { params } = context;
    const apiKey = params.headers['x-api-key'];
    if (!apiKey) {
      throw new Forbidden('API key is required');
    }

    // Re-fetch key and permissions for every request
    const keyRecord = await params.app.service('api-keys').get(apiKey, params);
    if (!keyRecord || keyRecord.revoked) {
      throw new Forbidden('Invalid or revoked API key');
    }

    // Re-validate tenant/resource association immediately before use
    const item = await service.get(context.id, context.params);
    if (item.organizationId !== keyRecord.organizationId) {
      throw new Forbidden('API key does not permit access to this resource');
    }

    return item;
  };
};

Example 2: Using a custom hook that re-validates within a transaction-like flow

Create a hook that performs validation and, if possible, keeps the operation within a single logical flow. While FeathersJS does not provide database transactions across all adapters, you can minimize state changes by avoiding intermediate writes that can alter key associations.

// hooks/validate-api-key.js
const { Forbidden } = require('@feathersjs/errors');

module.exports = function () {
  return async context => {
    const apiKey = context.params.headers['x-api-key'];
    if (!apiKey) {
      throw new Forbidden('API key missing');
    }

    // Fetch key with necessary relations in one call
    const keyInfo = await context.app.service('api-keys').get(apiKey, context.params, {
      query: { $select: ['id', 'organizationId', 'scopes', 'revoked'] }
    });

    if (keyInfo.revoked) {
      throw new Forbidden('API key revoked');
    }

    // Attach minimal, verified metadata for downstream use
    context.params.verifiedOrgId = keyInfo.organizationId;
    context.params.scopes = keyInfo.scopes;
    return context;
  };
};

Example 3: Securing mutable key bindings with immediate re-check

If your API allows updating key bindings, ensure that any sensitive operation re-checks the binding immediately before acting, rather than trusting earlier attachments.

// services/data/service.js
module.exports = function (service) {
  return async context => {
    const requestedId = context.id;
    const apiKey = context.params.headers['x-api-key'];

    const keyRecord = await context.app.service('api-keys').get(apiKey, context.params);
    // Re-validate right before data access
    const record = await service.get(requestedId, context.params);
    if (record.tenantId !== keyRecord.tenantId) {
      throw new (require('@feathersjs/errors').Forbidden)('Tenant mismatch');
    }
    return record;
  };
};

These examples emphasize re-validation at the point of use, avoiding reliance on stale metadata, and keeping authorization checks tightly coupled with the operation. They align with best practices for mitigating TOCTOU and reduce the risk of BOLA/IDOR and BFLA-like issues in FeathersJS APIs using API keys.

Frequently Asked Questions

Can middleware-based key validation alone prevent TOCTOU in FeathersJS?
No. Middleware validation that attaches metadata to params can create a timing gap if the data backing the key can change before the service uses that metadata. Re-validate immediately before use.
Does using the middleBrick CLI reduce TOCTOU risk for FeathersJS APIs?
middleBrick scans can surface findings related to authentication and BOLA/IDOR that may indicate split validation logic. Use findings to guide atomic re-validation patterns, but the scanner does not fix the implementation.