HIGH side channel attackfeathersjsapi keys

Side Channel Attack in Feathersjs with Api Keys

Side Channel Attack in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability

A side channel attack in a FeathersJS service that uses API keys occurs when information about the validity, existence, or rate status of a key leaks through timing differences, error messages, or observable behavior. Attackers infer whether a given key is valid or rate-limited without directly compromising the key itself.

FeathersJS is a framework that typically exposes services over REST or Socket.io. If API key validation is performed inside service hooks, the way errors are handled and the time taken to respond can create observable differences between valid, invalid, and rate-limited keys.

Consider a hook that verifies an API key by querying a database or a key-management store. If the hook uses asynchronous operations, the timing of the database lookup can vary: a valid key may require a read and permission checks, while an invalid key might short-circuit earlier. These timing differences can be measured by an attacker making repeated requests and observing response latency, potentially inferring which keys exist.

Error messages also contribute to information leakage. If a FeathersJS service returns distinct HTTP status codes or response bodies for different failure modes—an invalid key returns 401 with a message like “Invalid API key”, while a missing key returns 401 with “API key required”, or a rate-limited key returns 429 with a specific header—an attacker can distinguish among these cases. In the context of API keys, this means an attacker can learn whether a guessed key is syntactically malformed, valid but unauthorized, or valid and active but rate-limited.

Additionally, if service methods expose behavior that differs when an API key is valid (e.g., returning more verbose logs, enabling debug endpoints, or allowing higher throughput), this behavioral difference becomes a side channel. For example, a service might skip certain validation steps for trusted internal keys, leading to faster responses for authorized keys versus unauthorized ones. Combined with rate limiting, an attacker probing key usage windows might infer rate-limit boundaries by observing when responses begin to throttle or change character.

These side channels do not require authentication bypass; they exploit observable metadata around the API key handling path. In FeathersJS, because services are often dynamically routed and hooks centralize logic, inconsistent error handling or timing across hooks can unintentionally expose these channels. Mitigations focus on making validation paths and responses uniform, introducing constant-time checks where feasible, and ensuring that rate limiting and error responses do not reveal key validity.

Api Keys-Specific Remediation in Feathersjs — concrete code fixes

To mitigate side channel risks with API keys in FeathersJS, ensure that key validation is performed in a consistent manner, error responses are uniform, and timing characteristics do not depend on key validity.

First, centralize key validation in a hook that always performs the same steps regardless of key validity. Avoid early returns with different status codes for different errors. Instead, standardize on a generic 401 response and log the specifics server-side for audit purposes.

// src/hooks/validate-api-key.js
module.exports = function () {
  return async context => {
    const { accessToken } = context.params.headers || {};
    if (!accessToken) {
      // Do not reveal whether the endpoint requires a key; still go through verification flow
      context.params.apiKey = null;
      return context;
    }

    // Constant-time lookup simulation: always query, even if token appears malformed
    const keyStore = context.app.service('api-keys');
    const keyRecord = await keyStore.find({ query: { token: accessToken } });
    const key = (keyRecord && keyRecord.data && keyRecord.data[0]) || null;

    // Attach key metadata for downstream use; do not branch behavior on validity
    context.params.apiKey = key;

    // Optionally enforce rate limiting here using a separate, consistent step
    return context;
  };
};

Second, use a uniform error response from the service so that the response body and headers do not indicate whether the key was recognized. Configure FeathersJS to handle errors consistently.

// src/hooks/error-handler.js
module.exports = function () {
  return function errorHandler(err, context) {
    // Do not expose stack traces in production
    const isProduction = context.params.$meta && context.params.$meta.env === 'production';
    const statusCode = err.status || 500;
    const message = (statusCode === 401) ? 'Unauthorized' : 'Internal error';

    // Ensure headers are consistent; avoid custom headers that hint at key state
    context.result = {
      message,
      code: statusCode
    };
    context.statusCode = statusCode;
    return context;
  };
};

Third, apply rate limiting in a way that does not signal key validity. Apply limits uniformly across requests, and avoid returning distinct rate-limit headers for different key classes in a way that reveals categorization.

// src/hooks/rate-limit.js
const rateLimit = require('feathers-rate-limit');

module.exports = function () {
  return rateLimit({
    windowMs: 60 * 1000, // 1 minute
    max: 100, // limit each IP / key bucket
    keyExtractor: context => {
      // Use a stable identifier; if API key is present, use it, else fallback
      return (context.params.headers && context.params.headers.accessToken) || context.ip;
    },
    skipSuccessfulRequests: false, // count all requests to keep timing consistent
    handler: (req, res, /*next*/) => {
      res.status(429).json({
        message: 'Too many requests'
      });
    }
  });
};

Finally, register these hooks in your service configuration so they run for every request, ensuring that the validation path and timing remain predictable.

// src/services/notes/notes.hooks.js
const validateApiKey = require('../../hooks/validate-api-key');
const errorHandler = require('../../hooks/error-handler');
const rateLimit = require('../../hooks/rate-limit');

module.exports = {
  before: {
    all: [validateApiKey(), rateLimit()],
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  },
  after: {
    all: [],
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  },
  error: {
    all: [errorHandler()],
    find: [],
    get: [],
    create: [],
    update: [],
    patch: [],
    remove: []
  }
};

By making validation, error handling, and rate limiting consistent and independent of key validity, you reduce the feasibility of inferring key state through timing or response differences, thereby mitigating side channel risks specific to API key usage in FeathersJS.

Frequently Asked Questions

Does middleBrick test for side channel vulnerabilities like timing leaks around API keys?
middleBrick runs 12 security checks in parallel, including checks that can surface timing-related anomalies and error handling inconsistencies. However, it detects and reports findings with remediation guidance; it does not fix or block behavior.
Can the middleBrick CLI or GitHub Action fail a build if side channel risks are detected?
The middleBrick GitHub Action can fail builds if a security score drops below your threshold, but the tool reports findings—remediation guidance is provided, while fixing or blocking is outside its scope.