Broken Access Control in Feathersjs with Hmac Signatures
Broken Access Control in Feathersjs with Hmac Signatures
Broken Access Control in a FeathersJS service that uses Hmac Signatures occurs when authorization checks are incomplete or bypassed, allowing an authenticated client to access or modify resources belonging to another entity. FeathersJS is a framework-agnostic real-time API layer; when combined with Hmac-based authentication (e.g., an API key and secret used to sign requests), misconfiguration can expose endpoints to IDOR and BOLA even though the request is signed.
Consider a Feathers service for user profiles where Hmac Signatures validate the API key and a user identifier (user_id) is included in the signed payload. If the server verifies the Hmac but then retrieves the profile by an incoming path parameter (e.g., /profiles/:id) without ensuring that the authenticated user_id matches the requested :id, an attacker can change :id to another user’s identifier and read or modify data. This is a BOLA/IDOR flaw rooted in broken access control: the signature proves authenticity and integrity of the payload but does not enforce scope-based authorization.
An example vulnerable Feathers service might look like this, where the Hmac verification middleware adds user_id to params but the find/remove methods do not enforce ownership:
const crypto = require('crypto');
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
function verifyHmac(req, res, next) {
const signature = req.headers['x-api-signature'];
const timestamp = req.headers['x-api-timestamp'];
const apiSecret = process.env.API_SECRET; // shared secret
const payload = `${timestamp}.${req.method}.${req.originalUrl}`;
const expected = 'sha256=' + crypto.createHmac('sha256', apiSecret).update(payload).digest('hex');
if (!signature || signature !== expected) {
return res.status(401).send('Invalid signature');
}
// Assume timestamp window check happens here
req.user = { id: req.query.user_id }; // user_id is part of signed payload
next();
}
const app = express(feathers());
app.use('/profiles', {
async find(params) {
// Vulnerable: no check that params.user.id matches the requested profile
return db.find({ query: params.query });
},
async get(id, params) {
// Vulnerable: attacker can change id to another profile
return db.get(id);
}
});
app.use(verifyHmac);
In this setup, an attacker with a valid Hmac-signed request for their own profile can change the id to another user’s identifier and retrieve or alter data. The signature validates the client’s identity and request integrity, but the service lacks ownership scoping, resulting in broken access control. This maps to OWASP API Top 10 #1 Broken Object Level Authorization and can expose PII or allow privilege escalation if sensitive endpoints are improperly guarded.
Rate limiting and authentication checks may pass, but without explicit authorization that ties the authenticated subject to the resource, the API remains vulnerable. Even with Hmac Signatures ensuring request authenticity, developers must enforce that each operation validates the subject-resource relationship at the data access layer.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
To remediate Broken Access Control when using Hmac Signatures in FeathersJS, enforce that every data access operation validates ownership or scope using the identity derived from the signature. Do not rely on the client-supplied identifier alone; instead, inject the authenticated subject into params and use it to filter queries or verify ownership in get/update/remove handlers.
Below is a secure pattern where the Hmac middleware attaches user claims to params and each service method ensures the resource belongs to the authenticated subject:
const crypto = require('crypto');
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
function verifyHmac(req, res, next) {
const signature = req.headers['x-api-signature'];
const timestamp = req.headers['x-api-timestamp'];
const apiSecret = process.env.API_SECRET;
const payload = `${timestamp}.${req.method}.${req.originalUrl}`;
const expected = 'sha256=' + crypto.createHmac('sha256', apiSecret).update(payload).digest('hex');
if (!signature || signature !== expected) {
return res.status(401).send('Invalid signature');
}
// Ensure timestamp is within allowed window to prevent replay
const requestTime = parseInt(timestamp, 10);
const allowedSkew = 300; // 5 minutes
if (Math.abs(Date.now() / 1000 - requestTime) > allowedSkew) {
return res.status(401).send('Timestamp expired');
}
// user_id is part of signed payload; validate it exists
if (!req.query.user_id) {
return res.status(401).send('Missing user identifier');
}
req.user = { id: req.query.user_id };
next();
}
const app = express(feathers());
app.use(verifyHmac);
app.use('/profiles', {
async find(params) {
// Ensure queries are scoped to the authenticated user
return db.find({ query: { ...params.query, user_id: params.user.id } });
},
async get(id, params) {
// Enforce ownership: retrieve only if user_id matches
const record = await db.get(id);
if (!record || record.user_id !== params.user.id) {
throw new Error('Not found');
}
return record;
},
async create(data, params) {
// Enforce ownership on creation
data.user_id = params.user.id;
return db.create(data);
},
async patch(id, data, params) {
// Ensure update targets the authenticated user’s record
const existing = await db.get(id);
if (!existing || existing.user_id !== params.user.id) {
throw new Error('Not found');
}
return db.patch(id, data);
},
async remove(id, params) {
const existing = await db.get(id);
if (!existing || existing.user_id !== params.user.id) {
throw new Error('Not found');
}
return db.remove(id);
}
});
This approach binds each operation to the authenticated user_id extracted from the Hmac-signed payload, ensuring that even if an attacker guesses or iterates identifiers, they cannot access or modify resources outside their scope. For list endpoints, always filter by user_id in the query; for single-item endpoints, re-check ownership after retrieval. Combine this with server-side rate limiting and strict timestamp validation to reduce replay risks inherent in Hmac schemes.
When integrating with broader API security practices, middleBrick can scan endpoints using Hmac Signatures to detect missing ownership checks and other access control gaps. Its findings map to compliance frameworks such as OWASP API Top 10 and PCI-DSS, and remediation guidance is provided with severity and prioritized steps. The CLI tool (middlebrick scan