Auth Bypass in Sails with Hmac Signatures
Auth Bypass in Sails with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Sails is a Node.js web framework that encourages rapid API development. When Hmac Signatures are used for request authentication, developers often sign a subset of the request properties (method, URL, selected headers, and a timestamp) and send the signature in a custom header. If the server-side policy does not enforce strict validation, an attacker can manipulate unsigned or weakly validated parameters to bypass intended authorization checks.
An Auth Bypass occurs when an authenticated context is assumed based on the presence of a valid Hmac Signature, but the signature scope is too narrow. For example, if the signature covers only the HTTP method and path but omits critical identifiers like the resource ID or the user ID, an attacker can reuse a captured signature for a different resource or escalate privileges across related endpoints. This is especially relevant in Sails because controllers often rely on implicit model associations; if an endpoint uses a user-supplied id parameter without verifying ownership, a reused Hmac Signature may still be accepted while granting access to another user’s data.
Another common pattern in Sails is to sign a canonical string built from selected headers and the request body. If the body or selected headers are not consistently required for every endpoint, an attacker may send a valid signed request to an endpoint where those components are missing or altered, causing the server to accept the request as legitimate. Additionally, if the server does not enforce strict timestamp validation or allows a large replay window, intercepted Hmac-signed requests can be replayed to perform actions on behalf of the original signer.
Insecure default configurations in Sails applications can exacerbate these issues. For example, if route policies rely on a simple role flag present in the request without cross-checking the Hmac context, an attacker who discovers or guesses an unsigned but authorized route may be able to pivot to endpoints that should require the Hmac Signature. The interplay between permissive CORS settings, inconsistent policy application across controllers, and missing integrity checks on the signed parameters creates conditions where authentication can be bypassed despite the presence of Hmac Signatures.
To detect this category of issue, scanners perform black-box testing by sending the same Hmac-signed request to related endpoints with modified identifiers, and by replaying captured requests with slight variations in parameters. They also check whether the signature scope includes all security-critical elements such as resource IDs, tenant identifiers, and user context. Without these protections, Sails APIs can expose an Auth Bypass that is difficult to identify without targeted, protocol-aware testing.
Hmac Signatures-Specific Remediation in Sails — concrete code fixes
Remediation focuses on ensuring the Hmac Signature covers all context required to enforce authorization, and that server-side verification is strict and consistent across controllers.
Example: Secure Hmac Signing and Verification in Sails
Below is a complete, realistic example showing how to generate and verify an Hmac Signature in a Sails API, including key components that must be signed to prevent bypass.
// api/services/hmacService.js
const crypto = require('crypto');
module.exports = {
signPayload: function(payload, secret) {
const stringToSign = [
payload.method || 'GET',
payload.path || '',
payload.timestamp || Date.now().toString(),
payload.nonce || crypto.randomBytes(16).toString('hex'),
payload.bodyHash || '',
(payload.identifiers || []).join(':')
].join('|');
return crypto.createHmac('sha256', secret).update(stringToSign).digest('hex');
},
verifyPayload: function(payload, receivedSignature, secret) {
const expectedSignature = this.signPayload(payload, secret);
// Use timing-safe compare
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(receivedSignature)
);
}
};
In your controller, include critical identifiers in the payload and require them on every verified request:
// api/controllers/RecordController.js
module.exports = {
update: async function(req, res) {
const { id } = req.params;
const body = req.allParams();
const secret = sails.config.custom.hmacSecret;
const payload = {
method: req.method,
path: `/records/${id}`,
timestamp: req.headers['x-timestamp'],
nonce: req.headers['x-nonce'],
bodyHash: hashBody(body),
identifiers: [sails.models.record.getTenantId(req), id, req.session.userId || '']
};
const signature = req.headers['x-signature'];
if (!hmacService.verifyPayload(payload, signature, secret)) {
return res.unauthorized();
}
// Ensure ownership before proceeding
const record = await Record.findOne(id);
if (!record || record.userId !== payload.identifiers[2]) {
return res.forbidden('Access denied to this resource');
}
// Proceed with update
return res.ok(await Record.updateOne(id).set(body));
}
};
function hashBody(body) {
const crypto = require('crypto');
return crypto.createHash('sha256').update(JSON.stringify(body)).digest('hex');
}
Key remediation practices:
- Include the resource identifier (e.g., record ID) and tenant or user context in the signed string.
- Require the same set of signed parameters on every endpoint that enforces authorization.
- Use
crypto.timingSafeEqualfor signature comparison to prevent timing attacks. - Validate and parse timestamps on the server to mitigate replay attacks; reject requests with excessive clock skew.
- Ensure nonces are unique and optionally short-lived to prevent reuse within the same context.
- Apply these checks consistently across all controllers and policies; avoid relying on route-level flags that do not validate the Hmac scope.
By tying the Hmac Signature to the full security context — method, path, identifiers, and body — Sails applications can avoid Auth Bypass scenarios where unsigned or weakly scoped signatures grant unintended access.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |