MEDIUM insufficient loggingfeathersjs

Insufficient Logging in Feathersjs

How Insufficient Logging Manifests in Feathersjs

Insufficient logging in Feathersjs applications creates blind spots that attackers exploit during reconnaissance and post-exploitation phases. The framework's service-based architecture and event-driven nature mean logging gaps can hide critical security events.

A common manifestation occurs in authentication failures. Many Feathersjs apps use the built-in authentication service but fail to log failed login attempts. Without logging, you miss brute-force attacks targeting user credentials. Consider this vulnerable pattern:

app.service('authentication').create({
  strategy: 'local',
  email: '[email protected]',
  password: 'password123'
}); // No logging of failed attempts

Another critical gap appears in service method calls. Feathersjs services expose find, get, create, update, patch, and remove methods. Without logging, you cannot detect unauthorized data access or enumeration attempts:

// Vulnerable: No logging of service access
app.service('users').find({
  query: { isAdmin: true } // Attacker probing for admin accounts
});

Event-driven hooks present another logging challenge. Feathersjs hooks run before, after, or on errors for service methods. If hooks that modify sensitive data lack logging, you lose audit trails:

// Vulnerable hook with no logging
app.service('payments').hooks({
  before: {
    patch: context => {
      if (context.data.status === 'fraudulent') {
        context.data.locked = true;
        // Missing: log the fraud detection and account lock
      }
    }
  }
});

WebSocket connections via Feathersjs real-time features create additional blind spots. Without logging connection attempts and disconnections, you cannot detect connection flooding or unauthorized real-time data access:

// Missing: log WebSocket authentication failures
const io = require('socket.io')(server);
io.use((socket, next) => {
  const token = socket.handshake.query.token;
  if (!validateToken(token)) {
    // Missing: log failed authentication attempt
    return next(new Error('Authentication failed'));
  }
  next();
});

Configuration changes represent another overlooked area. Feathersjs apps often modify security settings at runtime through configuration APIs. Without logging these changes, you cannot detect configuration tampering:

// Vulnerable: configuration changes not logged
app.configure(require('feathers-authentication'))
  .defaults({
    header: 'Authorization',
    schemes: ['jwt', 'local']
  });
// Missing: log when authentication schemes change

Feathersjs-Specific Detection

Detecting insufficient logging in Feathersjs requires examining both the application code and runtime behavior. Start with static code analysis to identify services and hooks that lack logging statements.

For authentication-related logging gaps, search for authentication service usage without corresponding log statements. The following pattern indicates a logging deficiency:

// Vulnerable: authentication without logging
const auth = require('feathers-authentication');
app.configure(auth({
  local: {
    entity: 'user',
    service: 'users',
    usernameField: 'email',
    passwordField: 'password'
  }
}));
// Missing: log successful and failed authentication attempts

Service method calls require logging at the service level. Use Feathersjs's extensibility to add logging middleware:

const { BadRequest, Forbidden, NotFound } = require('@feathersjs/errors');

module.exports = function loggingMiddleware() {
  return async context => {
    const { method, type, params, path } = context;
    
    // Log all service calls
    console.log(`Service ${path}.${method} called`, {
      timestamp: new Date().toISOString(),
      user: params.provider ? 'external' : 'internal',
      method: type,
      data: context.data
    });
    
    // Log errors specifically
    if (context.error) {
      console.error(`Service error in ${path}.${method}`, {
        error: context.error.message,
        stack: context.error.stack,
        timestamp: new Date().toISOString()
      });
    }
  };
}

// Apply to all services
app.hooks({
  before: loggingMiddleware(),
  after: loggingMiddleware(),
  error: loggingMiddleware()
});

middleBrick's scanning approach specifically targets Feathersjs logging gaps. The scanner examines service definitions and hook chains to identify unlogged sensitive operations. For authentication services, middleBrick checks for:

  • Missing login attempt logging
  • Unlogged password reset operations
  • Missing session management logging

For data access patterns, middleBrick analyzes:

  • Service methods without audit trail logging
  • Hook chains that modify data without logging
  • Missing authorization failure logging

Dynamic analysis with middleBrick involves scanning your Feathersjs API endpoints to detect unlogged sensitive operations. The scanner sends test requests that trigger various service methods and checks for corresponding log entries or audit trail creation.

# Scan a Feathersjs API with middleBrick
middlebrick scan https://api.example.com

# Results show logging gaps:
# - Authentication service: FAILED (no login attempt logging)
# - Users service: FAILED (no audit trail for data access)
# - Payments service: FAILED (no fraud detection logging)

Feathersjs-Specific Remediation

Remediating insufficient logging in Feathersjs requires implementing comprehensive logging across the application's service layer, authentication mechanisms, and event-driven components.

Start with authentication logging using Feathersjs's extensibility. Create a dedicated authentication logging hook:

const winston = require('winston');
const logger = winston.createLogger({
  transports: [
    new winston.transports.File({ filename: 'auth.log' })
  ]
});

module.exports = function authLoggingHook() {
  return async context => {
    const { type, method, result, error } = context;
    const timestamp = new Date().toISOString();
    
    if (type === 'before' && method === 'create' && 
        context.path === 'authentication') {
      const { strategy, email } = context.data;
      logger.info(`Auth attempt: ${strategy} ${email}`, {
        timestamp,
        ip: context.params.ip,
        userAgent: context.params.headers['user-agent']
      });
    }
    
    if (type === 'after' && method === 'create' && 
        context.path === 'authentication') {
      const success = !context.error;
      const userId = success ? result.data.user.id : null;
      logger.info(`Auth ${success ? 'success' : 'failure'}`, {
        timestamp,
        userId,
        email: context.data.email,
        strategy: context.data.strategy,
        ip: context.params.ip
      });
    }
    
    if (error && context.path === 'authentication') {
      logger.error(`Auth error: ${error.message}`, {
        timestamp,
        email: context.data?.email,
        strategy: context.data?.strategy,
        ip: context.params.ip,
        stack: error.stack
      });
    }
  };
}

// Apply to authentication service
app.service('authentication').hooks({
  before: authLoggingHook(),
  after: authLoggingHook(),
  error: authLoggingHook()
});

Implement comprehensive service logging using Feathersjs's hook system. Create a generic logging hook that captures all service operations:

const { BadRequest, Forbidden, NotFound } = require('@feathersjs/errors');
const logger = require('./logger'); // Your configured logger

module.exports = function auditLoggingHook(options = {}) {
  const { ignorePaths = [] } = options;
  
  return async context => {
    const { type, method, path, params, result, error } = context;
    const timestamp = new Date().toISOString();
    const userId = params.user?.id || 'anonymous';
    const ip = params.ip || 'unknown';
    
    // Skip ignored paths
    if (ignorePaths.includes(path)) return context;
    
    // Log before operations
    if (type === 'before') {
      logger.info(`Service ${path}.${method} called`, {
        timestamp,
        userId,
        ip,
        method: type,
        data: context.data,
        query: context.params.query
      });
    }
    
    // Log after operations
    if (type === 'after') {
      logger.info(`Service ${path}.${method} completed`, {
        timestamp,
        userId,
        ip,
        method: type,
        result: result.data || result
      });
    }
    
    // Log errors
    if (error) {
      logger.error(`Service ${path}.${method} error`, {
        timestamp,
        userId,
        ip,
        error: error.message,
        code: error.code,
        stack: error.stack
      });
    }
    
    return context;
  };
}

// Apply to all services
app.hooks({
  before: auditLoggingHook(),
  after: auditLoggingHook(),
  error: auditLoggingHook()
});

For real-time WebSocket connections, implement logging for Feathersjs socket.io integration:

const io = require('socket.io')(server);
const logger = require('./logger');

io.use((socket, next) => {
  const timestamp = new Date().toISOString();
  const ip = socket.handshake.headers['x-forwarded-for'] || 
             socket.conn.remoteAddress;
  
  logger.info('WebSocket connection attempt', {
    timestamp,
    ip,
    userAgent: socket.handshake.headers['user-agent']
  });
  
  next();
});

io.on('connection', socket => {
  const timestamp = new Date().toISOString();
  const ip = socket.handshake.headers['x-forwarded-for'] || 
             socket.conn.remoteAddress;
  
  logger.info('WebSocket connected', {
    timestamp,
    socketId: socket.id,
    ip,
    userAgent: socket.handshake.headers['user-agent']
  });
  
  socket.on('disconnect', reason => {
    logger.info('WebSocket disconnected', {
      timestamp,
      socketId: socket.id,
      ip,
      reason,
      userAgent: socket.handshake.headers['user-agent']
    });
  });
});

Configure centralized logging with structured data for better analysis:

const winston = require('winston');
const { combine, timestamp, printf } = winston.format;

const logFormat = printf(({ level, message, timestamp, ...meta }) => {
  return JSON.stringify({
    level,
    message,
    timestamp,
    ...meta
  });
});

const logger = winston.createLogger({
  level: 'info',
  format: combine(
    timestamp(),
    logFormat
  ),
  transports: [
    new winston.transports.File({ filename: 'api.log' }),
    new winston.transports.Console()
  ]
});

module.exports = logger;

Frequently Asked Questions

How does middleBrick detect insufficient logging in Feathersjs applications?
middleBrick performs black-box scanning of your Feathersjs API endpoints, sending test requests to authentication and service endpoints. It analyzes the responses and checks for corresponding log entries or audit trail creation. The scanner specifically looks for missing login attempt logging, unlogged service method calls, and absent audit trails for sensitive operations. middleBrick provides a security score with detailed findings showing exactly where logging gaps exist in your Feathersjs application.
What Feathersjs-specific logging patterns should I implement?
Implement logging hooks for all service methods using Feathersjs's hook system, add authentication attempt logging for the authentication service, log WebSocket connection events for real-time features, and create audit trails for data modification operations. Use structured logging with timestamps, user IDs, IP addresses, and operation details. Apply logging consistently across before, after, and error hook types to capture the complete lifecycle of service operations.