HIGH data exposurefeathersjs

Data Exposure in Feathersjs

How Data Exposure Manifests in Feathersjs

Data exposure in Feathersjs applications commonly occurs through several framework-specific patterns. The most prevalent is inadequate service configuration where developers inadvertently expose sensitive data through default service behaviors.

Consider a typical Feathersjs service setup:

class UserService {
  async find(params) {
    return await this.Model.find(params.query);
  }

  async get(id, params) {
    return await this.Model.findById(id);
  }
}

The issue here is that these methods return complete database documents without filtering. If your User model includes fields like passwordHash, ssn, or paymentInfo, they'll be exposed to any authenticated user who can access the service.

Another common pattern is improper use of Feathersjs hooks. Developers often forget that hooks execute in sequence, and a hook that should restrict data might be placed after a hook that already exposed it:

const userService = app.service('users');

// WRONG: This hook runs too late
userService.hooks({
  after: {
    find: [maskSensitiveData]
  }
});

// This hook runs before and exposes everything
userService.hooks({
  before: {
    find: [addRelationships]
  }
});

Feathersjs's flexible query syntax can also lead to data exposure through overly permissive query parameters. A service that accepts arbitrary query objects without validation might allow clients to retrieve data they shouldn't access:

// Vulnerable: No query validation
async find(params) {
  return await this.Model.find(params.query);
}

// Malicious query that could expose data:
{
  "$or": [
    {"role": "admin"},
    {"ssn": {"$ne": null}}
  ]
}

Authentication context mishandling is another Feathersjs-specific issue. The framework's params.user object is often assumed to be present without proper null checks:

// Vulnerable: No authentication check
async remove(id, params) {
  const user = await this.Model.findById(id);
  if (user.id === params.user.id) {
    return await this.Model.remove(id);
  }
  throw new Error('Unauthorized');
}

// If params.user is undefined, this throws an exception
// rather than properly handling the unauthorized case

Feathersjs-Specific Detection

Detecting data exposure in Feathersjs applications requires examining both the service code structure and runtime behavior. The framework's convention-over-configuration approach means certain patterns are strong indicators of potential issues.

Start by analyzing your service files for these red flags:

# Look for services without data filtering
grep -r "async find" services/ | 
  grep -v "filter" | 
  grep -v "select" | 
  grep -v "projection"

# Find hooks that might expose data before filtering
grep -r "after:" hooks/ | 
  grep -E "(find|get|update|patch|remove)"

middleBrick's scanning approach is particularly effective for Feathersjs applications because it tests the actual API endpoints rather than just static code analysis. When you scan a Feathersjs API endpoint, middleBrick:

  1. Identifies the framework by examining response headers and JSON structure
  2. Tests authentication bypass scenarios specific to Feathersjs patterns
  3. Attempts to access protected fields through query manipulation
  4. Verifies proper authentication context handling

For example, scanning a Feathersjs user service might reveal:

{
  "endpoint": "https://api.example.com/users",
  "framework": "Feathersjs",
  "tests_performed": [
    "Authentication bypass detection",
    "Sensitive field exposure (password, ssn, payment info)",
    "Query parameter manipulation",
    "Authentication context validation"
  ],
  "findings": [
    {
      "severity": "high",
      "category": "Data Exposure",
      "description": "UserService.find() returns all user fields including passwordHash and ssn",
      "remediation": "Add data filtering in service methods",
      "evidence": "Response contains sensitive fields: passwordHash, ssn, paymentInfo"
    }
  ]
}

The middleBrick CLI makes this process straightforward:

npm install -g middlebrick
middlebrick scan https://api.example.com/users

# Or integrate into your CI/CD
middlebrick scan --fail-below B https://api.example.com

For comprehensive coverage, scan all your Feathersjs service endpoints:

for service in users posts messages; do
  middlebrick scan https://api.example.com/$service
done

Feathersjs-Specific Remediation

Remediating data exposure in Feathersjs requires leveraging the framework's built-in features while following security best practices. The most effective approach combines service-level filtering with hook-based data masking.

Service-level filtering prevents sensitive data from being queried in the first place:

class UserService {
  async find(params) {
    const query = params.query || {};
    
    // Filter out sensitive fields at the query level
    return await this.Model.find(query)
      .select('-passwordHash -ssn -paymentInfo -creditCard')
      .lean();
  }

  async get(id, params) {
    const query = params.query || {};
    
    return await this.Model.findById(id, '-passwordHash -ssn -paymentInfo')
      .lean();
  }

  async create(data, params) {
    // Remove sensitive fields from input
    const { passwordHash, ssn, ...safeData } = data;
    return await this.Model.create(safeData);
  }
}

Hooks provide an additional layer of protection and can handle more complex scenarios:

const { disallow, discard, when, isProvider } = require('feathers-hooks-common');

const maskSensitiveData = discard('passwordHash', 'ssn', 'paymentInfo');
const requireAuth = require('feathers-authentication-hooks').requireAuth;

module.exports = {
  before: {
    all: [
      // Require authentication for all methods
      requireAuth(),
      
      // Validate query parameters
      context => {
        const allowedFields = ['name', 'email', 'role'];
        if (context.params.query) {
          Object.keys(context.params.query).forEach(key => {
            if (!allowedFields.includes(key) && !key.startsWith('$')) {
              throw new Error(`Invalid query field: ${key}`);
            }
          });
        }
      }
    ],
    
    find: [
      // Add default filters
      context => {
        context.params.query = context.params.query || {};
        context.params.query.active = { $ne: false };
      }
    ]
  },
  
  after: {
    all: [
      // Mask sensitive data from all responses
      when(
        context => context.method !== 'create',
        maskSensitiveData
      )
    ]
  }
};

For complex data exposure scenarios involving relationships, use Feathersjs's populate hook with careful field selection:

const { authenticate } = require('feathers-authentication').hooks;
const { populate } = require('feathers-mongoose').hooks;

module.exports = {
  before: {
    find: [
      authenticate('jwt'),
      populate({
        include: [
          {
            service: 'posts',
            nameAs: 'posts',
            parentField: '_id',
            childField: 'author',
            query: {
              $select: ['title', 'content', 'createdAt']
            }
          }
        ]
      })
    ]
  }
};

Implement role-based data exposure control using custom hooks:

const checkRole = require('feathers-authentication-hooks').checkRole;

const restrictAdminFields = context => {
  if (context.params.user && context.params.user.role === 'admin') {
    return;
  }
  
  // Remove admin-only fields from response
  if (context.result) {
    if (Array.isArray(context.result)) {
      context.result.forEach(item => {
        delete item.role;
        delete item.lastLogin;
      });
    } else {
      delete context.result.role;
      delete context.result.lastLogin;
    }
  }
};

module.exports = {
  before: {
    find: [
      checkRole({
        roles: ['admin', 'user'],
        fieldName: 'role'
      })
    ]
  },
  after: {
    all: [
      restrictAdminFields
    ]
  }
};

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

How does middleBrick detect data exposure in Feathersjs APIs?
middleBrick performs active scanning by sending test requests to your Feathersjs endpoints and analyzing the responses. It looks for sensitive fields like password hashes, SSNs, and payment information in the JSON responses, tests authentication bypass scenarios, and verifies that query parameters are properly validated. The scanner identifies Feathersjs specifically by examining response patterns and headers, then applies framework-specific tests to uncover data exposure vulnerabilities.
Can I scan my Feathersjs API with middleBrick during development?
Yes, middleBrick is designed for development and CI/CD integration. You can scan your local Feathersjs API using the CLI tool with middlebrick scan http://localhost:3030/users. The Pro plan includes GitHub Action integration, allowing you to automatically scan your Feathersjs API in pull requests and fail builds if security scores drop below your threshold. This helps catch data exposure issues before they reach production.