Insecure Direct Object Reference in Feathersjs with Hmac Signatures
Insecure Direct Object Reference in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
In FeathersJS, an Insecure Direct Object Reference (IDOR) occurs when an API endpoint exposes a resource identifier (such as a database ID) without verifying that the authenticated subject has permission to access that specific resource. When Hmac Signatures are used for request authentication, IDOR can still manifest if the signature is tied only to identifying parameters (e.g., an id in the query) and does not also encode authorization context (tenant, owner, scope). An attacker who can control the resource identifier can change it in the request while the Hmac Signature remains valid because the signature may not cover the access-control-relevant dimensions of the request.
Consider a Feathers service that uses Hmac Signatures to ensure request integrity. A typical signing flow includes a timestamp, a nonce, and selected query parameters (such as id) to produce the signature. If the server uses the signed id to directly fetch a record—e.g., app.service('invoices').get(id)—without confirming the requesting user’s relationship to that invoice, an attacker can iterate through numeric or predictable IDs and retrieve other users’ invoices. This is IDOR on the resource identifier path, and the presence of Hmac Signatures does not prevent it; the signature merely ensures the request was not tampered with, not that the request is authorized.
In a real-world scenario, an API might accept query parameters like id and userId, sign both, and then retrieve the record by id alone. If userId is present but not enforced as an access boundary, an attacker could modify id while keeping userId unchanged, and the server might still locate a record outside the user’s scope. Because FeathersJS does not automatically enforce ownership or tenant boundaries, developers must explicitly incorporate access-control checks that tie the resource to the actor making the request. The vulnerability is not in Hmac cryptography but in the lack of a canonical, signature-covered authorization check that maps the resource to the requester’s permissions.
Another subtle IDOR risk arises when Hmac Signatures are computed over a subset of query parameters that omit contextual identifiers such as organizationId or teamId. An attacker can send a validly signed request with a manipulated id but a legitimate contextual identifier, and the server may resolve a cross-tenant reference. Even when the signature validates, the server must recompute or re-derive access rules using the same parameters that were signed, and ensure that the subject derived from authentication (e.g., from JWT or session) is compared against the resource’s owner or ACL. Without this, the combination of predictable resource identifiers and permissive lookup logic creates an IDOR vector that Hmac Signatures alone cannot close.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
To mitigate IDOR when using Hmac Signatures in FeathersJS, include authorization-relevant identifiers in the signed payload and enforce ownership or access control at the service layer. Do not rely on the signature to imply authorization; instead, couple signature validation with explicit checks that the authenticated subject has the right to the requested resource.
Below is a minimal, realistic example of Hmac signing in a FeathersJS client and server. The client builds a canonical string that includes the resource identifier and a user-specific scope, signs it, and sends both the identifier and the signature. The server validates the signature using the same canonical construction, then verifies that the subject associated with the request is allowed to access the identified resource.
Client-side signing (Node.js)
const crypto = require('crypto');
function buildSignedRequest({ userId, id, timestamp, nonce, secret }) {
const canonical = [
'POST',
'/invoices',
`userId=${userId}`,
`id=${id}`,
`timestamp=${timestamp}`,
`nonce=${nonce}`
].join('&');
const signature = crypto.createHmac('sha256', secret).update(canonical).digest('hex');
return { canonical, signature };
}
// Example usage
const secret = 'shared-secret';
const userId = 'usr_123';
const id = 'inv_456';
const timestamp = Date.now().toString();
const nonce = Math.random().toString(36).slice(2);
const { canonical, signature } = buildSignedRequest({ userId, id, timestamp, nonce, secret });
console.log({ canonical, signature, userId, id, timestamp, nonce });
Server-side verification and authorization in FeathersJS
const crypto = require('crypto');
function verifyHmac(req, secret) {
const { userId, id, timestamp, nonce, signature } = req.query;
if (!userId || !id || !timestamp || !nonce || !signature) {
throw new Error('Missing Hmac parameters');
}
const canonical = [
'POST',
'/invoices',
`userId=${userId}`,
`id=${id}`,
`timestamp=${timestamp}`,
`nonce=${nonce}`
].join('&');
const expected = crypto.createHmac('sha256', secret).update(canonical).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
throw new Error('Invalid Hmac signature');
}
return { userId, id };
}
// Feathers service hook
app.service('invoices').hooks({
before: {
async find(hook) {
const { user } = hook.params;
const { id } = hook.params.query;
const verified = verifyHmac(hook.params.raw || {}, process.env.HMAC_SECRET);
// Ensure the invoice belongs to the requesting user
const invoice = await app.service('invoices').Model.findOne({
where: { id: verified.id, userId: verified.userId }
});
if (!invoice || invoice.userId !== verified.userId) {
throw new Error('Not found');
}
// Proceed with safe, scoped lookup
hook.params.query.$and = [
{ id: verified.id },
{ userId: verified.userId }
];
}
}
});
In this pattern, the Hmac covers both userId and id, binding the resource identifier to the subject. The server recomputes the canonical string and, after signature validation, performs a scoped database lookup that includes the user identifier. This prevents IDOR because a modified id will either fail signature verification (if the attacker does not know the canonical form) or fail the ownership check (if the attacker reuses a valid signature with a different ID but the same user context).
For services where user context is derived from authentication rather than query parameters, include a stable subject ID in the canonical string and ensure the service enforces that the resource’s owner matches the subject. If you use organization or tenant identifiers, include them in the signed canonical to prevent cross-tenant IDOR. Rotate Hmac secrets periodically and keep timestamps within an acceptable window to reduce replay risks. Remember that middleBrick scans can validate whether your published OpenAPI spec and runtime behavior align on required authorization checks; use the CLI (middlebrick scan
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 |