Mass Assignment in Feathersjs with Hmac Signatures
Mass Assignment in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Mass Assignment in FeathersJS occurs when an API endpoint binds incoming HTTP payload fields directly to a model or database record without an allowlist. FeathersJS services commonly rely on frameworks like feathers-sequelize or feathers-mongoose, which may map payload keys to database columns automatically. When Hmac Signatures are used for authentication or integrity verification, developers may assume that verifying the signature is sufficient to ensure safe data handling. This assumption can create a false sense of security: the Hmac ensures the request has not been tampered with in transit, but it does not limit which fields a client is allowed to set.
Consider a Feathers service that accepts user profile updates. If the service uses a generic patch handler that maps all request body keys to the model without an explicit restrictedUpdateProperties or an allowlist, an attacker who obtains a valid Hmac-signed request (e.g., by observing a legitimate client interaction or through an insecure client-side implementation) can add or overwrite sensitive fields such as isAdmin, balance, or role. Because the Hmac is validated before the application applies the payload, the server may accept the maliciously extended object and persist unauthorized changes.
The vulnerability is specific to the combination of two factors: (1) the absence of property-level authorization and (2) the use of Hmac Signatures for request authentication. Hmac Signatures protect integrity and origin, but they do not provide authorization or input filtering. If the API relies heavily on Hmac to authenticate requests, developers might skip additional access controls or payload validation, inadvertently exposing endpoints to privilege escalation or data manipulation. This pattern is common in integrations where Hmac is used to sign webhook payloads or internal service calls, and the receiving Feathers service trusts the signed payload implicitly.
Real-world examples include endpoints that accept JSON objects for creation or update without whitelisting fields, especially when combined with services that support nested objects or relational updates. For instance, a /users/:id PATCH endpoint that passes the entire body to an ORM without filtering can allow an attacker to inject fields like permissions or verifiedAt. The presence of a valid Hmac does not mitigate this issue, as the signature only confirms the payload came from a trusted source, not that the source is permitted to modify those specific fields.
To address this securely, treat Hmac Signatures as one layer of assurance and always enforce property-level authorization on the server. Use explicit allowlists, avoid automatic mapping of all request fields, and validate each field against the user's permissions and the operation context. This approach ensures that even if a request is authenticated with a valid Hmac, only intended and safe properties are applied.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on strict property filtering and context-aware validation, independent of Hmac verification. Hmac should be used for integrity checks, but authorization and allowlisting must be applied explicitly in the service logic.
Example 1: Safe PATCH with Allowlist
Define an allowlist of updatable fields and use it in the service hook to sanitize the data before it reaches the ORM or database adapter.
// src/hooks/validate-patch.js
const allowedPatchFields = ['displayName', 'email', 'bio', 'avatarUrl'];
module.exports = function validatePatch() {
return function(context) {
if (context.method === 'patch' && context.data) {
const filtered = {};
allowedPatchFields.forEach(field => {
if (Object.prototype.hasOwnProperty.call(context.data, field)) {
filtered[field] = context.data[field];
}
});
context.data = filtered;
}
return context;
};
};
Apply the hook in your service definition:
// src/services/user/user.service.js
const { Service } = require('feathers-sequelize');
const validatePatch = require('./hooks/validate-patch');
class UserService extends Service {
constructor(options) {
super(options);
this.hooks({
before: {
patch: [validatePatch()],
// other hooks such as authentication and Hmac verification can remain
}
});
}
}
module.exports = function setupService(app) {
const userService = new UserService({ Model: app.get('models').User });
app.use('/users', userService);
};
Example 2: Safe CREATE with Role-Based Restrictions
When creating resources, prevent clients from setting privileged fields such as isAdmin or role by removing them from the payload if supplied, or by ignoring them entirely.
// src/hooks/restrict-create-fields.js
module.exports = function restrictCreateFields() {
return function(context) {
if (context.method === 'create' && context.data) {
// Remove fields that should never be set by the client during creation
delete context.data.isAdmin;
delete context.data.role;
delete context.data.apiKey;
// Optionally, set defaults based on server-side logic
context.data.status = 'active';
}
return context;
};
};
Combine with Hmac verification in the authentication hook to ensure requests are both authentic and safe:
// src/hooks/hmac-verify.js (simplified)
const crypto = require('crypto');
const SHARED_SECRET = process.env.HMAC_SECRET;
module.exports = function verifyHmac() {
return function(context) {
const signature = context.headers['x-api-signature'];
const payload = JSON.stringify(context.data || context.params.query || {});
const expected = crypto.createHmac('sha256', SHARED_SECRET).update(payload).digest('hex');
if (signature !== expected) {
throw new Error('Invalid HMAC signature');
}
return context;
};
};
Register hooks in order so that Hmac verification runs before field restriction, ensuring only valid, authorized requests proceed to sanitization:
// src/services/orders/orders.service.js
const { Service } = require('feathers-sequelize');
const verifyHmac = require('../../hooks/hmac-verify');
const restrictCreateFields = require('../../hooks/restrict-create-fields');
class OrderService extends Service {
constructor(options) {
super(options);
this.hooks({
before: {
all: [verifyHmac()],
create: [restrictCreateFields()],
patch: [restrictCreateFields()]
}
});
}
}
module.exports = function setupService(app) {
const orderService = new OrderService({ Model: app.get('models').Order });
app.use('/orders', orderService);
};
These examples show how to combine Hmac-based integrity checks with explicit property control. Hmac verification ensures the request has not been altered, while allowlisting and field removal ensure that only intended properties are applied, preventing Mass Assignment regardless of signature validity.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |