HIGH insufficient loggingfeathersjsapi keys

Insufficient Logging in Feathersjs with Api Keys

Insufficient Logging in Feathersjs with Api Keys — how this creates or exposes the vulnerability

FeathersJS is a framework for real-time web applications that commonly uses REST and Socket.io transports. When API keys are used for authentication, developers often focus on validating and rotating keys but overlook how requests tied to those keys are recorded. Insufficient logging means events such as authentication attempts, authorization failures, and unusual key usage are not captured in a structured, searchable way. This gap becomes significant when an attacker probes endpoints using stolen or misconfigured keys, because without detailed logs there is no reliable mechanism to detect patterns such as repeated failed authorizations, geographic anomalies, or high-volume consumption indicative of abuse.

In practice, this vulnerability arises from a combination of three dimensions: the FeathersJS application code, the API key implementation, and the logging strategy. If the application does not explicitly log key validation outcomes, user identity associated with the key, request paths accessed, and timestamps, security teams lose visibility into the kill chain. For example, an unauthenticated attack surface scan by middleBrick can surface endpoints that accept API keys but do not enforce strict logging around authorization decisions. Even when keys are validated via hooks, failing to log contextual metadata (such as the key ID, scope, and origin) means suspicious behavior blends into normal traffic. Attackers may exploit weak logging to perform low-and-slow abuse, knowing their activities will not trigger alerts. This intersects with broader API security checks such as Authentication, BOLA/IDOR, and Rate Limiting, where logs are essential for post-scan investigations and forensic analysis.

Complicating matters is that FeathersJS does not prescribe a single logging format; developers must integrate transports like Winston or Pino and ensure each hook emits structured events. Without explicit instrumentation, default error handling may capture only stack traces, omitting the API key context required to trace an incident back to a client or system. As a result, organizations struggle to correlate logs with findings from scanning tools that identify weak authentication or authorization configurations. Prioritized remediation therefore requires both code-level changes to emit precise logs and operational practices that retain sufficient detail for detection and response without violating privacy or retention policies.

Api Keys-Specific Remediation in Feathersjs — concrete code fixes

Remediation centers on instrumenting FeathersJS hooks and services to capture key-related metadata consistently, while preserving separation of concerns and avoiding accidental exposure of sensitive values. Below are concrete, syntactically correct examples showing how to log key validation outcomes, user identity, and request context within a typical FeathersJS service configuration.

Example 1: Logging in an authentication hook with API keys

const { AuthenticationError } = require('@feathersjs/errors');
const logger = require('../logger'); // Pino or Winston instance

module.exports = function (options = {}) {
  return async context => {
    const { accessToken, secret } = context.data;
    const { app } = context;

    if (!accessToken) {
      logger.warn({
        event: 'api_key_missing',
        path: context.path,
        method: context.method,
        timestamp: new Date().toISOString()
      });
      throw new AuthenticationError('API key is required');
    }

    // Validate API key via your provider (pseudo-implementation)
    const keyRecord = await app.service('api-keys').getByKey(accessToken);
    if (!keyRecord || keyRecord.status !== 'active') {
      logger.warn({
        event: 'api_key_invalid',
        keyId: accessToken ? accessToken.slice(-8) : 'none',
        path: context.path,
        method: context.method,
        timestamp: new Date().toISOString()
      });
      throw new AuthenticationError('Invalid API key');
    }

    // Successful authentication log
    logger.info({
      event: 'api_key_authenticated',
      keyId: accessToken ? accessToken.slice(-8) : 'none',
      userId: keyRecord.userId,
      scope: keyRecord.scope,
      path: context.path,
      method: context.method,
      timestamp: new Date().toISOString()
    });

    // Attach user to context for downstream services
    context.params.user = { id: keyRecord.userId, scope: keyRecord.scope };
    return context;
  };
};

Example 2: Logging within a custom service method for authorization

const logger = require('../logger');

class MessageService {
  async create(data, params) {
    const { user } = params;
    const { content } = data;

    // Example authorization check
    if (!user.scope.includes('messages:write')) {
      logger.warn({
        event: 'authorization_failure',
        userId: user.id,
        scope: user.scope,
        path: params.path,
        method: 'create',
        timestamp: new Date().toISOString()
      });
      throw new Error('Forbidden');
    }

    // Proceed with business logic
    const message = await super.create(data, params);
    logger.info({
      event: 'message_created',
      userId: user.id,
      messageId: message.id,
      path: params.path,
      method: 'create',
      timestamp: new Date().toISOString()
    });
    return message;
  }
}

module.exports = function () {
  const app = this;
  app.use('/messages', new MessageService());
};

Example 3: Centralized request logging via hooks

const logger = require('../logger');

const requestLogger = context => {
  const { path, method, params } = context;
  const { user } = params;
  logger.info({
    event: 'request',
    userId: user ? user.id : 'anonymous',
    path,
    method,
    query: params.query || {},
    timestamp: new Date().toISOString()
  });
  return context;
};

// Apply globally after authentication so user is available
module.exports = {
  before: [requestLogger],
  after: [requestLogger],
  error: [requestLogger]
};

These examples emphasize structured fields (event, keyId, userId, scope, path, method, timestamp) that enable correlation with external monitoring systems. In combination with middleBrick scans that validate Authentication and Authorization behaviors, such logging provides the visibility needed to detect abuse, support incident response, and refine security policies over time.

Frequently Asked Questions

What should I log when an API key fails authentication in FeathersJS?
Log the event name (e.g., api_key_invalid), a masked key identifier (last 8 characters), the request path, HTTP method, timestamp, and any available key metadata such as scope and userId if present. Avoid logging the full key value.
How can I ensure logs are structured and searchable in a FeathersJS application?
Use a logging library like Pino or Winston, emit JSON-formatted entries with consistent fields (event, keyId, userId, scope, path, method, timestamp), and apply hooks globally or at service level to standardize logs across the API.