Vulnerable Components in Feathersjs with Mongodb
Vulnerable Components in Feathersjs with Mongodb — how this specific combination creates or exposes the vulnerability
FeathersJS is a framework for real-time APIs that can use MongoDB through adapters such as feathers-mongodb. When the framework’s service layer is not explicitly constrained and the MongoDB adapter is configured with permissive query options, the unauthenticated attack surface can expose collection data or allow unintended data manipulation. Common insecure patterns include using the default find without a $filter for the authenticated subject, omitting server-side query normalization, and relying on client-supplied _id or query fields without validation.
BOLA/IDOR issues arise when a service endpoint like /users/:id directly uses params.query (which may include user-controlled filters) to build a MongoDB query without scoping to the requesting user. For example, if the client sends {_id: '...'} and the service does not enforce that the returned document belongs to the requester, an attacker can enumerate or access other users’ records. Similarly, BFLA/Privilege Escalation can occur when role or scope metadata is stored in the same collection and is not excluded from user-controlled queries, allowing low-privilege users to modify administrative flags.
Input validation gaps in FeathersJS hooks or MongoDB adapter options can lead to malformed queries or unintended field projection. If the adapter passes raw client input to find or aggregate, operators such as $where or improperly handled regular expressions might enable server-side behavior changes or injection-like results. Data exposure can also occur through over-projection; without explicitly limiting returned fields, sensitive fields like password hashes or internal status flags may be returned to the client.
Property Authorization and Unsafe Consumption intersect when the API allows clients to submit update objects that include operator keys (e.g., $set, $inc) that the application did not intend to expose. If the service merges client payload directly into an update descriptor without stripping or validating operators, a client can escalate privileges or alter unrelated properties. LLM/AI Security is relevant when model-generated API clients or admin assistants craft MongoDB queries from natural language without verifying scope, inadvertently exposing endpoints or weakening filters.
To detect these patterns with middleBrick, you can run a scan against your FeathersJS service endpoint. The scanner performs unauthenticated testing and maps findings to OWASP API Top 10 and compliance frameworks, providing prioritized remediation guidance rather than attempting to fix the API itself.
Mongodb-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on scoping queries to the authenticated subject, validating and sanitizing input, and explicitly controlling projection and update operators. Below are concrete code examples for a FeathersJS service using the MongoDB adapter.
1. Scope queries to the authenticated user (BOLA/IDOR mitigation)
Ensure every find and get applies a user filter. Do not rely on client-supplied IDs alone.
// services/users/users.service.js
const { Service } = require('feathers-mongodb');
class UserService extends Service {
async find(params) {
// params.user.id is set by your authentication hook
const { user } = params;
if (!user) {
throw new Error('Unauthenticated');
}
// Override query to scope to the requesting user
params.query = {
...params.query,
_id: { $in: user.roles && user.roles.admin ? await this.Model.distinct('_id') : [user._id] }
};
return super.find(params);
}
async get(id, params) {
const { user } = params;
if (!user) {
throw new Error('Unauthenticated');
}
// Ensure the requested document belongs to the user or is public
const record = await this.Model.findOne({ _id: id, userId: user._id });
if (!record) {
throw new Error('Not found');
}
return record;
}
}
module.exports = function () {
const app = this;
app.use('/users', new UserService({ Model: app.get('mongodb').db('mydb').collection('users') }));
};
2. Validate and sanitize input to prevent injection-like outcomes
Use hooks to validate query keys and remove unexpected fields before they reach the adapter.
// hooks/validate-query.js
module.exports = function validateQuery(options = {}) {
return async context => {
const allowedFields = new Set(['name', 'email', 'status', 'limit', 'skip', 'sort']);
if (context.params.query) {
for (const key of Object.keys(context.params.query)) {
if (!allowedFields.has(key)) {
delete context.params.query[key];
}
}
}
return context;
};
};
// In service setup
app.use('/data', new Service({
Model: mongoClient.db('db').collection('data')
}).hooks({
before: {
all: [validateQuery()],
find: [/* other hooks */]
}
}));
3. Explicitly control projection and update operators (Property Authorization)
Do not allow clients to specify $set with arbitrary keys. Whitelist allowed fields and strip operator keys from user payloads.
// hooks/sanitize-update.js
module.exports = function sanitizeUpdate(options = {}) {
return async context => {
const { user } = context.params;
if (context.method === 'patch' && context.data) {
const isAdmin = user && user.roles && user.roles.admin;
const allowedFields = new Set(isAdmin ? ['name', 'email', 'role', 'status'] : ['name', 'email']);
const update = { $set: {} };
for (const [key, value] of Object.entries(context.data)) {
if (allowedFields.has(key)) {
update.$set[key] = value;
}
}
if (Object.keys(update.$set).length === 0) {
throw new Error('No valid fields to update');
}
// Preserve _id if present in URL, but ensure it matches user context
context.data = update;
// Remove any operator keys sent by the client
delete context.data.$set;
delete context.data.$inc;
delete context.data.$push;
}
return context;
};
};
// Usage
app.use('/profile', new Service({
Model: mongoClient.db('db').collection('profiles')
}).hooks({
before: {
all: [sanitizeUpdate()]
}
}));
4. Apply consistent runtime filtering and projections
Remove internal fields from responses and enforce field-level permissions at the service layer.
// hooks/projection.js
module.exports = function projection(allowedPublic = ['name', 'email'], allowedAdmin = ['*']) {
return async context => {
const { user } = context.params;
const isAdmin = user && user.roles && user.roles.admin;
const allowed = isAdmin ? allowedAdmin : allowedPublic;
if (context.result && Array.isArray(context.result.data)) {
context.result.data = context.result.data.map(doc => {
const filtered = {};
for (const key of Object.keys(doc)) {
if (allowed.includes(key) || allowed.includes('*')) {
filtered[key] = doc[key];
}
}
return filtered;
});
} else if (context.result && typeof context.result.data === 'object') {
const doc = context.result.data;
const filtered = {};
for (const key of Object.keys(doc)) {
if (allowed.includes(key) || allowed.includes('*')) {
filtered[key] = doc[key];
}
}
context.result.data = filtered;
}
return context;
};
};
// Apply in service setup
app.use('/users', new UserService({ /* ... */ }).hooks({
after: {
all: [projection(['name', 'email'], ['*'])] // admins see all fields
}
}));
These patterns reduce the risk of BOLA/IDOR, BFLA/Privilege Escalation, and Data Exposure when using FeathersJS with MongoDB. middleBrick can scan your endpoints to surface misconfigurations; the CLI command middlebrick scan <url> provides findings and remediation guidance without requiring authentication or agents.