Insecure Design in Feathersjs with Mongodb
Insecure Design in Feathersjs with Mongodb — how this specific combination creates or exposes the vulnerability
FeathersJS is a framework that encourages rapid development of REST and real-time APIs. When paired with MongoDB as the data store, insecure design patterns commonly emerge at the architectural and service-layer level. Rather than relying on the framework to enforce authorization for every field and relation, developers may implement coarse-grained hooks or skip hooks entirely for convenience. This creates an insecure design where endpoints expose unsafe operations, allow over-permissive queries, or fail to enforce ownership and scope checks consistently.
Consider an authentication bypass design flaw: if the Feathers service for user profiles does not require authentication on sensitive operations (e.g., PATCH /users/{id}/role), an unauthenticated attacker can modify roles or escalate privileges. Even when authentication plugins are used, misconfigured hooks may skip role-based access controls, leading to privilege escalation via BOLA/IDOR. An example vulnerability pattern is permitting clients to supply an _id or userId in the request body and directly passing it to the MongoDB query without verifying that the authenticated subject owns that resource.
Insecure data exposure often arises from projection misuse. A Feathers service might return sensitive fields such as password hashes, reset tokens, or internal flags because the service does not explicitly restrict the projection sent to the client. With MongoDB, the projection parameter must be carefully defined to exclude sensitive fields. If a developer relies on schema defaults or omits projection logic, the API can unintentionally leak credentials or PII. This aligns with data exposure findings in the middleBrick LLM/AI Security and Data Exposure checks, which detect missing field-level controls and excessive data returned from endpoints.
Another insecure design involves embedding logic inside query filters that should be enforced server-side. For instance, a service might accept a query like { userId: req.user._id, isPublic: { $ne: true } } but mistakenly allow overriding the userId key via client-supplied query parameters, enabling horizontal privilege escalation across users. MongoDB’s query structure is flexible, but without strict validation and binding of the authenticated subject to the query, the API becomes vulnerable to IDOR and BOLA attacks. middleBrick’s BOLA/IDOR and Property Authorization checks are designed to surface these exact classes of design gaps.
Insecure design also manifests in validation and input handling. Feathers allows hooks to transform data before persistence; if these hooks do not rigorously validate ownership references and array operators, an attacker can inject crafted update payloads that modify unrelated records. Poorly designed $inc or $set operations that do not scope updates to the authenticated user’s tenant or organization can lead to privilege escalation or data leakage. middleBrick’s Input Validation and Property Authorization checks target these patterns by comparing runtime behavior against the OpenAPI spec and detecting unsafe mutations.
Finally, insecure design can include missing rate limiting and unsafe consumption patterns. Without explicit rate limiting at the Feathers service level, MongoDB-backed endpoints may be susceptible to enumeration or brute-force attacks, especially when error messages differ between valid and invalid identifiers. middleBrick’s Rate Limiting and Unsafe Consumption checks examine whether the API discloses information that could aid an attacker and whether operations allow unintended side effects without adequate guardrails.
Mongodb-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on enforcing ownership at the database query level, validating inputs, and tightening projections. Always bind the authenticated subject into the query filter rather than trusting client-supplied identifiers. Below are concrete, realistic code examples for securing Feathers services that use MongoDB.
1. Enforce ownership in query filters
Ensure every query includes the authenticated user’s ID. Do not allow the client to override the user identifier.
// Good: bind userId from authentication, ignore any client-supplied userId in params
app.service('profiles').hooks({
before: {
async get(context) {
const { user } = context.params;
if (!user || !user._id) {
throw new Error('Unauthenticated');
}
// Override any potentially tampered id in params.query
context.params.query = {
_id: context.id,
userId: user._id,
};
return context;
},
async find(context) {
const { user } = context.params;
if (!user || !user._id) {
throw new Error('Unauthenticated');
}
// Scope the MongoDB query to the authenticated user
context.params.query = {
userId: user._id,
isPublic: { $ne: true },
};
// Explicitly set projection to avoid leaking sensitive fields
context.params.query.$select = ['name', 'email', 'profileId'];
return context;
}
}
});
2. Use strict validation and whitelisting
Validate all incoming data and never directly pass client payloads into MongoDB update operations.
const { Validator } = require('fastest-validator');
const v = new Validator();
const profileSchema = {
profileId: 'string|empty:false',
displayName: 'string|trim|min:1|max:64',
avatarUrl: 'url|optional'
};
app.service('profiles').hooks({
before: {
async update(context) {
const payload = context.data;
const safeData = v.validate(payload, profileSchema);
if (safeData !== true) {
throw new Error(`Validation failed: ${JSON.stringify(safeData)}`);
}
// Scoped update: ensure only allowed fields are updated
context.data = {
$set: {
displayName: safeData.displayName,
avatarUrl: safeData.avatarUrl
}
};
// Do not allow $inc or $setOnInsert from client without strict checks
return context;
}
}
});
3. Explicit projections to prevent data exposure
Define projections that exclude sensitive fields such as password hashes and tokens.
app.service('users').hooks({
before: {
async get(context) {
// Ensure projection excludes sensitive fields
context.params.query = context.params.query || {};
context.params.query.$select = [
'profileId',
'displayName',
'email',
'roles'
];
return context;
},
async find(context) {
context.params.query = context.params.query || {};
context.params.query.$select = [
'profileId',
'displayName',
'email',
'roles'
];
return context;
}
}
});
4. Role-based access control within hooks
Implement role checks before allowing mutations that change permissions or sensitive fields.
app.service('profiles').hooks({
before: {
async patch(context) {
const { user, data } = context.params;
if (!user || !user.roles || !user.roles.includes('admin')) {
// Prevent non-admins from modifying roles or permissions
if (data && (data.roles !== undefined || data.permissions !== undefined)) {
throw new Error('Forbidden: insufficient permissions');
}
}
// Continue with scoped update
context.params.query = {
_id: context.id,
userId: user._id,
};
return context;
}
}
});
5. Combine with middleBrick checks
Use the middleBrick CLI to validate that your service design aligns with security best practices. Scan your API endpoints to detect missing projections, unsafe update patterns, and insufficient scope checks. The GitHub Action can enforce a security score threshold before deployment, and the MCP Server allows you to scan APIs directly from your IDE while iterating on designs.