Bola Idor in Feathersjs
How Bola Idor Manifests in Feathersjs
BOLA (Broken Object Level Authorization) and IDOR (Insecure Direct Object Reference) vulnerabilities in Feathersjs applications often stem from the framework's flexible service architecture and implicit assumptions about user context. Feathersjs services provide a uniform interface for data operations, but developers frequently overlook authorization checks when implementing custom methods or extending built-in functionality.
The most common manifestation occurs in Feathersjs's service methods where developers assume the current user context is automatically enforced. Consider a user service with a get method:
class UserService extends Service {
async get(id, params) {
return await this.Model.findById(id);
}
}
This implementation is vulnerable because it doesn't verify that the requesting user owns the resource or has permission to access it. An authenticated user could request any user ID and retrieve sensitive information like email addresses, phone numbers, or even hashed passwords if the model includes them.
Feathersjs's params.user object, populated by authentication hooks, is often incorrectly assumed to be sufficient for authorization. A common anti-pattern:
class MessageService extends Service {
async find(params) {
// Vulnerable: trusts params.user without validation
return await this.Model.find({
userId: params.user.id
});
}
}
This code appears secure but fails if params.user is manipulated or if the service is called internally without proper authentication context. Attackers can exploit this by crafting requests that bypass authentication layers or by finding internal service calls that don't properly sanitize user input.
Feathersjs's dynamic service registration creates another attack vector. Services registered at runtime with user-provided identifiers can lead to path traversal or parameter injection:
app.use(`/${userId}/messages`, messageService);
If userId comes from user input without validation, attackers could access other users' message endpoints by manipulating the URL path.
Custom service methods are particularly vulnerable when they accept object identifiers directly:
class DocumentService extends Service {
async shareDocument(docId, targetUserId) {
const doc = await this.get(docId);
// Vulnerable: no check that current user owns doc
await this.Model.updateOne(
{ _id: docId },
{ $push: { sharedWith: targetUserId } }
);
}
}
This allows any authenticated user to share any document, potentially exposing confidential information to unauthorized parties.
Feathersjs's event system can also introduce BOLA vulnerabilities when events contain object references without proper access controls:
class NotificationService extends Service {
async create(data, params) {
// Vulnerable: doesn't verify user can access referenced objects
const notification = await super.create(data, params);
this.emit('created', notification);
return notification;
}
}
Without proper authorization checks, event listeners could process notifications containing references to objects the user shouldn't access.
Feathersjs-Specific Detection
Detecting BOLA/IDOR vulnerabilities in Feathersjs applications requires a systematic approach that examines both code patterns and runtime behavior. middleBrick's API security scanner is particularly effective at identifying these issues in Feathersjs applications because it understands the framework's service patterns and common authentication hooks.
Static code analysis for Feathersjs applications should focus on service method implementations. middleBrick scans for patterns like:
middlebrick scan https://api.example.com --framework=feathersjs
The scanner examines service files for missing authorization checks in common patterns. It looks for service methods that access models directly without verifying user permissions, particularly in get, find, update, and remove operations.
middleBrick specifically tests Feathersjs authentication patterns by attempting to access resources across different user contexts. It creates test accounts and systematically tries to access each other's data through the same service endpoints. For a vulnerable user service, it would:
- Create two test users (User A and User B)
- Authenticate as User A and attempt to access User B's data
- Authenticate as User B and attempt to access User A's data
- Report any successful cross-user data access
The scanner also examines Feathersjs's hook system, which is often used for authentication and authorization. It identifies hooks that might be bypassed or improperly configured:
// Pattern middleBrick detects as potentially vulnerable
hooks: {
before: {
all: [authenticate('jwt')],
get: [verifyOwnership],
find: [verifyOwnership]
}
}
middleBrick tests whether these hooks are properly enforced across all service methods and whether there are any paths that bypass authentication entirely.
Runtime testing with middleBrick involves sending requests with manipulated parameters to test for IDOR vulnerabilities. For example, it tests whether changing a user ID in the URL or request body allows access to unauthorized resources:
GET /users/507f1f77bcf86cd799439011 HTTP/1.1
Authorization: Bearer valid.jwt.token
The scanner systematically modifies the user ID to test if the application properly validates that the authenticated user matches the requested resource.
middleBrick also checks for Feathersjs-specific configuration issues that can lead to BOLA vulnerabilities, such as:
- Services registered with dynamic paths containing user input
- Missing authentication hooks on service methods
- Improper use of
params.querywithout sanitization - Services that allow internal calls without authentication context
The scanner's OpenAPI analysis feature is particularly valuable for Feathersjs applications, as it can detect discrepancies between the documented API and the actual runtime behavior, revealing potential authorization bypasses.
Feathersjs-Specific Remediation
Remediating BOLA/IDOR vulnerabilities in Feathersjs applications requires implementing proper authorization checks throughout your service layer. Feathersjs provides several native mechanisms for authorization that integrate well with its service architecture.
The most effective approach is using Feathersjs's built-in authorization hooks. The feathers-authentication-hooks package provides verifyPermissions and queryWithPermissions hooks that can be applied consistently across services:
const { authenticate } = require('@feathersjs/authentication').hooks;
const { queryWithPermissions } = require('feathers-authentication-hooks');
const userPermissions = {
service: 'users',
field: 'id',
permissions: {
administrator: [], // admins can access all
user: [ 'id', 'owner' ] // users can access own data
}
};
class UserService extends Service {
constructor(options) {
super(options);
this.hooks({
before: {
find: [ authenticate('jwt'), queryWithPermissions(userPermissions) ],
get: [ authenticate('jwt'), queryWithPermissions(userPermissions) ],
update: [ authenticate('jwt'), queryWithPermissions(userPermissions) ],
remove: [ authenticate('jwt'), queryWithPermissions(userPermissions) ]
}
});
}
}
This ensures that all service methods automatically filter results based on the authenticated user's permissions, preventing unauthorized access to other users' data.
For custom service methods that don't fit the standard CRUD patterns, implement explicit authorization checks:
class DocumentService extends Service {
async shareDocument(docId, targetUserId, { user }) {
// Verify ownership before sharing
const document = await this.Model.findById(docId);
if (!document || document.ownerId.toString() !== user.id) {
throw new errors.NotFound('Document not found or unauthorized');
}
// Additional business logic...
await this.Model.updateOne(
{ _id: docId },
{ $push: { sharedWith: targetUserId } }
);
}
}
This pattern ensures that users can only share documents they own, preventing the BOLA vulnerability where any user could share any document.
Feathersjs's context-based hooks system allows for fine-grained authorization that can be applied consistently:
const verifyDocumentOwnership = context => {
if (context.method === 'get' || context.method === 'remove') {
const { id, user } = context.params;
return context.app
.service('documents')
.get(id)
.then(doc => {
if (doc.ownerId.toString() !== user.id) {
throw new errors.Forbidden('Unauthorized access');
}
});
}
return context;
};
app.service('documents').hooks({
before: {
get: [ verifyDocumentOwnership ],
remove: [ verifyDocumentOwnership ]
}
});
This approach centralizes ownership verification logic, making it easier to maintain and ensuring consistent enforcement across your application.
For applications with complex authorization requirements, consider implementing a role-based access control (RBAC) system using Feathersjs's event system:
const checkPermissions = async (context, requiredPermission) => {
const { user } = context.params;
const userRole = await context.app
.service('roles')
.get(user.roleId);
if (!userRole.permissions.includes(requiredPermission)) {
throw new errors.Forbidden('Insufficient permissions');
}
};
// Apply to multiple services
const withPermission = permission => [
authenticate('jwt'),
context => checkPermissions(context, permission)
];
app.service('admin/reports').hooks({
before: {
find: withPermission('view-reports')
}
});
This pattern allows you to define permissions at the role level and enforce them consistently across your Feathersjs application.
Finally, implement comprehensive logging and monitoring for authorization failures. Feathersjs's error handling makes it easy to log unauthorized access attempts:
const logUnauthorizedAccess = error => {
if (error.name === 'Forbidden') {
console.warn(`Unauthorized access attempt: ${error.message}`);
// Log to monitoring service...
}
throw error;
};
app.hooks({
error: {
all: [ logUnauthorizedAccess ]
}
});
This helps you detect and respond to potential BOLA attacks in real-time.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |