HIGH broken access controlfeathersjs

Broken Access Control in Feathersjs

How Broken Access Control Manifests in Feathersjs

Broken Access Control in Feathersjs often stems from the framework's service-oriented architecture and default assumptions about data access. The most common manifestation occurs when developers rely on Feathersjs's default service methods without implementing proper authorization checks.

Consider a typical Feathersjs application with user management:

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

// Default behavior - anyone can access any record
app.service('messages').find(); // No auth checks by default

The framework's hooks system provides the primary defense mechanism, but misconfiguration leads to critical vulnerabilities. A common pattern is forgetting to apply the authenticate hook globally:

// Vulnerable - no authentication
app.service('users').find();

// Secure - requires authentication
app.service('users').before({
find: [ authenticate('jwt') ]
});

Feathersjs's polymorphic service structure creates another attack vector. A single service can handle multiple entity types, and developers might implement authorization checks that work for one entity but fail for another:

// Vulnerable - checks user ID but not message ownership
async function checkOwner(context) {
const { id, params } = context;
const message = await context.service.get(id);
if (message.userId !== params.user.id) {
throw new errors.Forbidden('Access denied');
}
return context;
}

// Secure - verifies both ownership and existence
async function secureCheckOwner(context) {
const { id, params } = context;
if (!id) return context; // Allow find() operations

const message = await context.service.get(id);
if (!message) {
throw new errors.NotFound('Message not found');
}

if (message.userId !== params.user.id) {
throw new errors.Forbidden('Access denied');
}
return context;
}

Relationship-based attacks are particularly effective in Feathersjs. When services use population or $populateParams, improper configuration can expose related records:

// Vulnerable - populates all comments regardless of ownership
app.service('posts').before({
find: [ populate({ schema: { comments: true } }) ]
});

// Secure - filters populated data by user
app.service('posts').before({
find: [ populate({
schema: { comments: true },
query: { userId: '$user.id' }
}) ]
});

Feathersjs-Specific Detection

Detecting Broken Access Control in Feathersjs applications requires understanding the framework's unique patterns and common misconfigurations. The most effective approach combines static analysis of hook configurations with dynamic testing of service endpoints.

Manual detection starts with examining your hook definitions. Look for services that lack authentication hooks or have incomplete authorization logic:

// Scan your app for vulnerable patterns
function auditHooks(app) {
const vulnerableServices = [];

app.services.forEach(service => {
const hooks = service._hooks || {};

// Check if authentication is missing
if (!hooks.before || !hooks.before.find ||
!hooks.before.find.some(h => h.name === 'authenticate')) {
vulnerableServices.push(service.name);
}
});

return vulnerableServices;
}

Dynamic testing reveals runtime access control issues that static analysis misses. Use Feathersjs's built-in testing utilities to simulate different user roles:

const { createApp } = require('@feathersjs/feathers');
const hooks = require('@feathersjs/hooks');

async function testAccessControl(app, servicePath) {
const service = app.service(servicePath);
// Test unauthenticated access
try {
await service.find();
console.log(`${servicePath}: No authentication required`);
} catch (error) {
console.log(`${servicePath}: Authentication enforced`);
}

// Test authenticated user with insufficient permissions
const user = { id: 1, role: 'user' };
try {
await service.find({ user });
console.log(`${servicePath}: Basic auth check passed`);
} catch (error) {
console.log(`${servicePath}: Auth check failed`);
}
}

For comprehensive automated detection, middleBrick's Feathersjs-specific scanning identifies these patterns across your entire API surface. The scanner tests unauthenticated endpoints, attempts privilege escalation through ID manipulation, and verifies that population parameters don't expose unauthorized data:

# CLI usage for Feathersjs APIs
middlebrick scan https://api.yourapp.com/messages --framework feathers

# GitHub Action integration
- uses: middlebrick/middlebrick-action@v1
with:
url: https://api.yourapp.com
framework: feathers
fail-on-severity: high

The scanner specifically tests Feathersjs patterns like params.user injection, population-based data exposure, and hook chain vulnerabilities that traditional scanners miss.

Feathersjs-Specific Remediation

Securing Feathersjs applications requires leveraging the framework's native capabilities while following security best practices. The most effective approach combines global authentication policies with granular authorization controls.

Start with a global authentication setup using Feathersjs's built-in mechanisms:

const { authenticate } = require('@feathersjs/authentication').hooks;
const { iff, isProvider } = require('feathers-hooks-common');

// Global authentication for external requests
app.service('users').hooks({
before: {
all: iff(
context => context.params.provider,
authenticate('jwt')
)
}
});

For authorization, implement role-based access control using Feathersjs hooks:

const errors = require('@feathersjs/errors');

function authorize(roles = []) {
return async context => {
const { user, method } = context.params;

if (!user) {
throw new errors.NotAuthenticated();
}

if (roles.length === 0) return context; // Allow any authenticated user

if (!roles.includes(user.role)) {
throw new errors.Forbidden('Insufficient permissions');
}

return context;
};
}

// Apply to specific services
app.service('admin').hooks({
before: {
all: [ authorize(['admin']) ]
}
});

Implement ownership verification for user-specific data:

const { fastJoin } = require('feathers-hooks-common');

const checkOwnership = ownerField => context => {
const { id, params } = context;
const userId = params.user?.id;

if (!userId) throw new errors.NotAuthenticated();

if (id) {
return context.service.get(id).then(record => {
if (record[ownerField] !== userId) {
throw new errors.Forbidden('Access denied');
}
return context;
});
}

return context;
};

// Secure user messages service
app.service('messages').hooks({
before: {
find: [ checkOwnership('userId') ],
get: [ checkOwnership('userId') ],
update: [ checkOwnership('userId') ],
patch: [ checkOwnership('userId') ],
remove: [ checkOwnership('userId') ]
}
});

For complex authorization scenarios, use Feathersjs's fastJoin hook to implement data-level access controls:

const resolver = {
joins: {
user: () => async (message, context) => {
const { user } = context.params;
if (message.userId !== user.id) {
message.user = null; // Hide user data
}
},
async (message, context) => {
const { user } = context.params;
message.comments = message.comments.filter(c =>
c.visibility === 'public' || c.userId === user.id
);
}
}
};

app.service('posts').hooks({
after: {
find: [ fastJoin(resolver) ]
}
});

Finally, integrate security scanning into your development workflow using middleBrick's Feathersjs-specific detection:

# Continuous scanning in CI/CD
- name: Scan API Security
run: middlebrick scan https://staging.yourapp.com --framework feathers
continue-on-error: true # Don't fail builds, but track results

This approach ensures that Broken Access Control vulnerabilities are caught early, before they reach production environments.

Frequently Asked Questions

How does Feathersjs's default behavior contribute to Broken Access Control?
Feathersjs services are open by default - they allow unauthenticated access to all methods unless you explicitly add authentication hooks. The framework assumes developers will implement security controls, but this leads to many applications exposing sensitive data through default service methods.
Can middleBrick detect Feathersjs-specific access control issues?
Yes, middleBrick includes Feathersjs-specific scanning that tests for common framework patterns like population-based data exposure, hook chain vulnerabilities, and IDOR attacks in service methods. It identifies issues that generic API scanners miss.