HIGH race conditionfeathersjsapi keys

Race Condition in Feathersjs with Api Keys

Race Condition in Feathersjs with Api Keys — how this combination creates or exposes the vulnerability

A race condition in a FeathersJS service combined with API key authentication can expose state-dependent behavior that leads to security or integrity issues. In FeathersJS, services are often implemented as JavaScript modules that rely on external data stores (e.g., databases or caches). When API keys are used for authentication, the framework typically validates the key before routing the request to the service. If the validation and the subsequent business logic are not performed atomically, an attacker can exploit timing differences between these steps.

Consider a scenario where an API key is checked for scope or rate limits in one step, and then a resource update (such as incrementing a usage counter or modifying permissions) occurs in a separate step. An attacker can send a high volume of authenticated requests so that the validation step succeeds for multiple requests before the first request’s state change is applied. This can result in privilege escalation, unauthorized access, or incorrect application state. For example, a key with read-only scope could be validated, but before the service enforces that scope, another request modifies the underlying resource, allowing the read-only key to perform write actions.

Real-world attack patterns resemble OWASP API Top 10 race condition risks, where lack of transactional guarantees leads to Insecure Direct Object References (IDOR) or Broken Function Level Authorization (BFLA). In FeathersJS, if a custom hook or service method queries a key’s permissions and then later performs an action without re-validating in a single atomic operation, the window between the query and the action becomes exploitable. Similarly, if an API key is accepted after a slow cache lookup but before a write completes, concurrent requests can bypass intended constraints.

The interaction with unauthenticated LLM endpoint detection is indirect: if an API key is accepted but the service exposes an endpoint that returns generated text or tool calls, a race could allow an attacker to probe timing to infer whether key validation succeeded. This does not mean the LLM security features are compromised, but that the surrounding service logic must ensure atomicity around key checks and downstream operations.

middleBrick scans such endpoints in black-box mode, testing unauthenticated attack surfaces where possible and flagging inconsistent authorization timing across the 12 security checks, including Authentication, BOLA/IDOR, and BFLA/Privilege Escalation. The scanner does not fix the implementation but provides prioritized findings with remediation guidance to help developers address timing-sensitive logic.

Api Keys-Specific Remediation in Feathersjs — concrete code fixes

To mitigate race conditions when using API keys in FeathersJS, ensure that authorization checks and state changes are performed atomically within a single service method or hook. Avoid splitting validation and mutation across separate asynchronous steps that can overlap. Below are concrete code examples that demonstrate secure patterns.

First, a minimal FeathersJS service using API keys with an insecure pattern that is susceptible to race conditions:

// Insecure example: validation and mutation are separate steps
app.service('accounts').hooks({
  before: {
    create: [context => {
      const { apiKey } = context.params.query;
      if (!apiKey) throw new Error('API key required');
      // Race: key validation and permission check happen before the update
      return fetchPermissions(apiKey).then(permissions => {
        context.meta.permissions = permissions;
      });
    }]
  },
  after: {
    create: [context => {
      // Race: permissions may have changed between before and after
      if (context.meta.permissions.canWrite) {
        return incrementUsage(context.params.accountId);
      }
      return Promise.resolve();
    }]
  }
});

The above pattern is vulnerable because the permission fetch and the usage increment are not atomic. An attacker can send concurrent requests that pass the initial check before the state updates, leading to BFLA or IDOR.

A secure approach uses a single transaction-like hook that validates and applies changes without exposing a window:

// Secure example: combine validation and mutation atomically
app.service('accounts').hooks({
  before: {
    create: [async context => {
      const { apiKey, accountId } = context.params.query;
      if (!apiKey) throw new Error('API key required');
      // Fetch permissions and apply change in one logical step
      const permissions = await fetchPermissions(apiKey);
      if (!permissions.canWrite) throw new Error('Unauthorized');
      // Perform the update immediately within the same hook
      await incrementUsage(accountId);
      // Optionally attach data for downstream hooks
      context.result = { updated: true };
      return context;
    }]
  }
});

Alternatively, if your data store supports transactions (e.g., a SQL database with Sequelize or TypeORM), wrap the key validation and the write in a transaction to guarantee atomicity:

// Transactional approach with TypeORM-like pattern
app.service('accounts').hooks({
  before: {
    create: [async context => {
      const { apiKey, accountId } = context.params.query;
      const manager = getEntityManager();
      return manager.transaction(async transactionalEntityManager => {
        const keyRecord = await transactionalEntityManager.findOne(ApiKey, { where: { value: apiKey } });
        if (!keyRecord || !keyRecord.permissions.canWrite) {
          throw new Error('Unauthorized');
        }
        await transactionalEntityManager.update(Account, accountId, { usage: () => 'usage + 1' });
        context.result = { updated: true };
      });
    }]
  }
});

Additional recommendations include using short-lived key tokens, avoiding mutable shared state between hooks when possible, and leveraging middleware that enforces strict ordering. middleBrick’s CLI can be used to scan endpoints from the terminal with middlebrick scan <url>, while the GitHub Action can add API security checks to your CI/CD pipeline to fail builds if risk scores drop below your chosen threshold.

For teams needing deeper visibility, the Pro plan offers continuous monitoring and the MCP Server allows scanning APIs directly from AI coding assistants, helping catch insecure patterns before they reach production.

Frequently Asked Questions

How can I verify that my FeathersJS API key checks are atomic and race-free?
Use a combination of code review focusing on single-hook atomicity, transaction usage where supported, and automated scans with a tool like middleBrick to detect timing inconsistencies in authorization flows.
Does middleBrick fix race conditions in FeathersJS services?
middleBrick detects and reports race conditions and related authorization timing issues, providing prioritized findings and remediation guidance. It does not modify or fix your code.