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:
- Identifies the framework by examining response headers and JSON structure
- Tests authentication bypass scenarios specific to Feathersjs patterns
- Attempts to access protected fields through query manipulation
- 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 ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |
Frequently Asked Questions
How does middleBrick detect data exposure in Feathersjs APIs?
Can I scan my Feathersjs API with middleBrick during development?
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.