Skip to content

Security: feathersjs/feathers-elasticsearch

Security

docs/SECURITY.md

Security Policy

Overview

This document outlines the security considerations, known issues, and best practices for using the Feathers Elasticsearch adapter in production environments.

Last Security Review: 2025-11-03
Last Security Update: 2025-11-03
Overall Risk Level: LOW (after v4.0.0 security improvements)
Production Ready: Yes


βœ… Security Features Implemented (v4.0.0)

The following security improvements have been implemented in version 4.0.0:

1. Query Depth Validation βœ…

  • What: Prevents stack overflow attacks via deeply nested queries
  • Default: Maximum depth of 50 levels
  • Configuration: security.maxQueryDepth
  • Impact: Blocks malicious queries like { $or: [{ $or: [...] }] } nested 1000+ levels deep

2. Bulk Operation Limits βœ…

  • What: Prevents DoS via mass update/delete operations
  • Default: Maximum 10,000 documents per bulk operation
  • Configuration: security.maxBulkOperations
  • Impact: Prevents accidental or malicious operations affecting millions of documents

3. Raw Method Whitelist βœ…

  • What: Restricts which Elasticsearch API methods can be called via raw()
  • Default: All methods disabled (empty whitelist)
  • Configuration: security.allowedRawMethods
  • Impact: BREAKING CHANGE - Must explicitly enable raw methods needed

4. Query String Sanitization βœ…

  • What: Prevents regex DoS attacks in $sqs (simple query string) operator
  • Default: Validates against catastrophic backtracking patterns, 500 char limit
  • Configuration: security.maxQueryStringLength
  • Impact: Blocks patterns like /.*.*.*.* that cause CPU exhaustion

5. Security Configuration API βœ…

  • What: Centralized security settings with sensible defaults
  • Access: Via service.security property
  • Configuration: Pass security object in service options

πŸ”§ Security Configuration

Configure security settings when creating the service:

import { Client } from '@elastic/elasticsearch'; import service from 'feathers-elasticsearch'; const client = new Client({ node: 'http://localhost:9200' }); app.use('/my-service', service({ Model: client, index: 'my-index', // Security configuration security: { // Query complexity limits maxQueryDepth: 50, // Max nesting for $or/$and/$nested (default: 50) maxArraySize: 10000, // Max items in $in/$nin arrays (default: 10000) // Bulk operation limits maxBulkOperations: 10000, // Max documents in bulk patch/remove (default: 10000) // Document size limits maxDocumentSize: 10485760, // 10MB max document size (default: 10MB) // Query string limits for $sqs maxQueryStringLength: 500, // Max length of $sqs queries (default: 500) // Raw method whitelist (IMPORTANT: empty by default = all disabled) allowedRawMethods: [ 'search', // Allow search operations 'count', // Allow count operations // 'indices.delete', // DON'T enable destructive operations! ], // Cross-index query restrictions allowedIndices: [], // Empty = only service's index allowed // Or specify: ['index1', 'index2'] // Field restrictions for $sqs queries searchableFields: [], // Empty = all fields searchable // Or specify: ['name', 'email', 'bio'] // Error verbosity enableDetailedErrors: false, // true in dev, false in production // Input sanitization enableInputSanitization: true, // Prevent prototype pollution } }));

Default Security Settings

If you don't provide a security configuration, these defaults are used:

{ maxQueryDepth: 50, maxArraySize: 10000, maxBulkOperations: 10000, maxDocumentSize: 10485760, // 10MB maxQueryStringLength: 500, allowedRawMethods: [], // ⚠️ ALL RAW METHODS DISABLED allowedIndices: [], // Only default index allowed searchableFields: [], // All fields searchable enableDetailedErrors: process.env.NODE_ENV !== 'production', enableInputSanitization: true }

Security Review Summary

A comprehensive security review identified no critical vulnerabilities. The high-severity issues found have been addressed in v4.0.0.

Security Status After v4.0.0

  • βœ… Query depth validation - RESOLVED
  • βœ… Bulk operation limits - RESOLVED
  • βœ… Raw method whitelist - RESOLVED
  • βœ… Query string sanitization - RESOLVED
  • βœ… TypeScript strict mode enabled - Excellent type safety
  • βœ… No code injection vulnerabilities - No use of eval(), new Function(), etc.
  • βœ… Strong input validation patterns - Consistent use of validateType()
  • ⚠️ Information disclosure - Error messages detailed in dev mode (by design)
  • ℹ️ Index name validation - Optional, configure via security.allowedIndices

πŸ”΄ High Severity Issues (RESOLVED in v4.0.0)

1. Unrestricted Raw Elasticsearch API Access

Status: βœ… RESOLVED in v4.0.0
Severity: HIGH
Component: raw() method

Description:
The raw() method allows arbitrary Elasticsearch API calls without authentication, authorization, or input validation. This can be exploited to delete indices, modify cluster settings, or access unauthorized data.

Resolution:
As of v4.0.0, the raw() method is disabled by default. All raw methods are blocked unless explicitly whitelisted via security.allowedRawMethods.

Migration Guide:

If your application uses raw(), you must whitelist the methods:

// v3.x - raw() was unrestricted app.use('/elasticsearch', service({ Model: client, // ... other options })); // v4.0+ - Must whitelist methods app.use('/elasticsearch', service({ Model: client, security: { allowedRawMethods: ['search', 'count'] // Only allow safe read operations } })); app.service('elasticsearch').hooks({ before: { raw: [disallow('external')] // Block from external clients } });

Option B - Implement strict whitelist:

const ALLOWED_RAW_METHODS = new Set(['search', 'count', 'explain']); app.service('elasticsearch').hooks({ before: { raw: [ context => { const method = context.arguments[0]; if (!ALLOWED_RAW_METHODS.has(method)) { throw new errors.MethodNotAllowed(`Method '${method}' is not allowed`); } } ] } });

2. Elasticsearch Query DSL Injection

Status: Known Issue
Severity: HIGH
Component: $sqs (simple query string) operator

Description:
The $sqs operator accepts user-controlled query strings passed directly to Elasticsearch without sanitization, potentially allowing query injection attacks or regex DoS.

Mitigation:

// Add validation hook app.service('elasticsearch').hooks({ before: { find: [ context => { const { query } = context.params; if (query && query.$sqs) { // Validate query string length if (query.$sqs.$query.length > 500) { throw new errors.BadRequest('Query string too long'); } // Prevent regex patterns that could cause catastrophic backtracking if (/\/\.\*(\.\*)+/.test(query.$sqs.$query)) { throw new errors.BadRequest('Invalid query pattern'); } // Whitelist allowed fields const allowedFields = ['name', 'description', 'tags']; const requestedFields = query.$sqs.$fields || []; for (const field of requestedFields) { const cleanField = field.replace(/\^.*$/, ''); if (!allowedFields.includes(cleanField)) { throw new errors.BadRequest(`Field '${field}' is not searchable`); } } } } ] } });

3. Denial of Service via Unbounded Operations

Status: Known Issue
Severity: HIGH
Components: Bulk patch, bulk remove, complex queries

Description:
Several operations lack safeguards against resource exhaustion:

  • No maximum limit on bulk operations (could patch/remove millions of documents)
  • No query timeout enforcement
  • No validation on deeply nested queries

Mitigation:

app.service('elasticsearch').hooks({ before: { find: [ // Limit query complexity context => { const depth = getQueryDepth(context.params.query); if (depth > 50) { throw new errors.BadRequest('Query too complex'); } } ], patch: [ // Restrict bulk patches async context => { if (context.id === null) { // This is a bulk operation - check how many documents would be affected const count = await context.service.find({ ...context.params, paginate: false, query: { ...context.params.query, $limit: 0 } }); const maxBulk = 1000; if (count.total > maxBulk) { throw new errors.BadRequest( `Bulk operation would affect ${count.total} documents, maximum is ${maxBulk}` ); } } } ], remove: [ // Restrict bulk deletes (or disable entirely) context => { if (context.id === null) { throw new errors.MethodNotAllowed('Bulk deletes not allowed'); } } ] } }); // Helper function to calculate query depth function getQueryDepth(query, depth = 0) { if (!query || typeof query !== 'object') return depth; let maxDepth = depth; for (const key of Object.keys(query)) { if (key === '$or' || key === '$and') { const value = query[key]; if (Array.isArray(value)) { for (const item of value) { maxDepth = Math.max(maxDepth, getQueryDepth(item, depth + 1)); } } } } return maxDepth; }

🟑 Medium Severity Issues

4. Sensitive Information Disclosure in Errors

Status: Known Issue
Severity: MEDIUM
Component: Error handler

Description:
Detailed Elasticsearch error information is returned to clients, potentially exposing internal system details like index structure, field names, and cluster configuration.

Mitigation:

app.service('elasticsearch').hooks({ error: { all: [ context => { if (process.env.NODE_ENV === 'production') { // Log full error server-side console.error('Elasticsearch error:', context.error); // Return generic message to client if (context.error.details) { delete context.error.details; } if (context.error.stack) { delete context.error.stack; } // Use generic messages const genericMessages = { 400: 'Invalid request parameters', 404: 'Resource not found', 409: 'Resource conflict', 500: 'Internal server error' }; const status = context.error.code || 500; context.error.message = genericMessages[status] || genericMessages[500]; } } ] } });

5. Missing Index Name Validation

Status: Known Issue
Severity: MEDIUM
Component: $index filter

Description:
The $index filter allows users to specify arbitrary index names without validation, potentially enabling cross-index data access.

Mitigation:

// Option A - Disable $index filter entirely (recommended) app.use('/elasticsearch', service({ Model: client, index: 'my-index', filters: { $index: undefined // Remove $index filter } })); // Option B - Implement index whitelist const allowedIndices = ['my-index', 'my-index-staging']; app.service('elasticsearch').hooks({ before: { all: [ context => { const requestedIndex = context.params.query?.$index; if (requestedIndex && !allowedIndices.includes(requestedIndex)) { throw new errors.Forbidden(`Access to index '${requestedIndex}' is not allowed`); } } ] } });

6. Prototype Pollution Risk

Status: Known Issue
Severity: MEDIUM
Component: Object operations in multiple files

Description:
User-controlled object properties could potentially be used for prototype pollution attacks through document data or query parameters.

Mitigation:

// Sanitize input data function sanitizeObject(obj) { if (!obj || typeof obj !== 'object') return obj; const dangerous = ['__proto__', 'constructor', 'prototype']; const sanitized = {}; for (const key of Object.keys(obj)) { if (dangerous.includes(key)) { continue; // Skip dangerous keys } const value = obj[key]; sanitized[key] = typeof value === 'object' && value !== null ? sanitizeObject(value) : value; } return sanitized; } app.service('elasticsearch').hooks({ before: { create: [ context => { context.data = sanitizeObject(context.data); } ], update: [ context => { context.data = sanitizeObject(context.data); } ], patch: [ context => { context.data = sanitizeObject(context.data); } ] } });

7. Dependency Vulnerabilities

Status: Known Issue
Severity: MEDIUM (Development only)
Component: Development dependencies

Description:
npm audit identified 9 vulnerabilities in development dependencies. These do NOT affect production runtime but should be addressed for secure development environments.

Mitigation:

# Update dependencies npm audit fix # For unfixable issues, consider removing dtslint if not actively used npm uninstall dtslint # Add audit to CI/CD npm audit --production # Only check production dependencies

🟒 Low Severity Issues

8. Missing Rate Limiting

Applications should implement rate limiting at the Feathers hooks level to prevent abuse.

9. Missing Request Size Limits

Document size validation should be added for create/update operations.

10. Query Cache Memory Usage

The WeakMap cache could grow indefinitely in long-running processes. Consider implementing an LRU cache with TTL.


πŸ›‘οΈ Production Deployment Security Checklist

Required Actions

  • Disable or restrict raw() method access
  • Implement bulk operation limits (max 1,000-10,000 documents)
  • Add query complexity validation
  • Sanitize error messages in production
  • Validate or disable $index filter
  • Implement input sanitization for all create/update operations
  • Run npm audit fix for development environment

Recommended Actions

  • Enable authentication on all service methods
  • Implement authorization hooks (e.g., feathers-casl)
  • Add rate limiting
  • Configure Elasticsearch client with SSL/TLS
  • Set request timeouts (30 seconds recommended)
  • Enable audit logging for sensitive operations
  • Implement document size validation
  • Add field whitelisting for $sqs queries
  • Set up automated security scanning in CI/CD

Environment Configuration

# Required environment variables NODE_ENV=production ELASTICSEARCH_URL=https://your-cluster:9200 ES_USERNAME=app_user ES_PASSWORD=strong_password # Security settings MAX_BULK_OPERATIONS=1000 MAX_QUERY_DEPTH=50 MAX_DOCUMENT_SIZE=10485760 # 10MB ENABLE_RAW_METHOD=false

πŸ”’ Elasticsearch Client Security

Configure your Elasticsearch client with security best practices:

import { Client } from '@elastic/elasticsearch'; const client = new Client({ node: process.env.ELASTICSEARCH_URL, // Authentication auth: { username: process.env.ES_USERNAME, password: process.env.ES_PASSWORD }, // SSL/TLS ssl: { rejectUnauthorized: true, // Verify certificates ca: fs.readFileSync('./ca.crt'), // CA certificate }, // Performance and DoS protection maxRetries: 3, requestTimeout: 30000, // 30 second timeout sniffOnConnectionFault: false, // Prevent node enumeration maxSockets: 10, // Limit concurrent connections maxFreeSockets: 5 });

πŸ“Š Security Metrics

Category Count Status
Critical Issues 0 βœ… None found
High Severity 3 ⚠️ Mitigations documented
Medium Severity 4 ⚠️ Mitigations documented
Low Severity 3 ℹ️ Optional improvements
Code Coverage 94.21% βœ… Excellent
TypeScript Strict Mode Enabled βœ… Excellent

πŸ› Reporting Security Vulnerabilities

If you discover a security vulnerability in this package, please report it by:

  1. DO NOT open a public GitHub issue
  2. Email the maintainers directly at: security@feathersjs.com
  3. Include:
    • Description of the vulnerability
    • Steps to reproduce
    • Potential impact
    • Suggested fix (if any)

We will respond within 48 hours and work with you to address the issue.


πŸ“š Additional Resources


πŸ“ Changelog

2025-11-03

  • Initial security review completed
  • Documented 3 high-severity issues
  • Documented 4 medium-severity issues
  • Added production deployment checklist
  • Created mitigation examples

Security is a shared responsibility. This document provides guidance, but each application must implement appropriate security controls based on its specific requirements and threat model.

There aren’t any published security advisories