Crlf Injection in Loopback with Hmac Signatures
Crlf Injection in Loopback with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject carriage return (CR, \r) and line feed (\n) characters into a header or status-line context. In Loopback applications, this often arises when user-controlled input is reflected into HTTP response headers, such as Location or custom headers, without proper sanitization. When Hmac Signatures are used—typically to verify the integrity of requests or webhook payloads—the presence of injected line breaks can alter the canonical string that is signed. If the server recomputes the Hmac over a message that includes injected \r\n sequences, the signature may still verify because the altered payload was intentionally constructed by the attacker. This mismatch between expected and actual canonical form can allow an attacker to smuggle headers, perform response splitting, or bypass origin checks that rely on header values. In a Loopback API that signs webhook events using Hmac, an attacker who controls a parameter such as a redirect URL or a user-supplied label may embed \r\n to inject a new header, such as X-Webhook-Delivery: forged. The server that validates the Hmac may still accept the request if it does not canonicalize line endings before signing, leading to security-relevant effects like unauthorized redirection or forged webhook processing.
Hmac Signatures-Specific Remediation in Loopback — concrete code fixes
To mitigate Crlf Injection when using Hmac Signatures in Loopback, ensure that any data used to construct the signed string is normalized and that header values are strictly validated. The following patterns demonstrate secure handling in a Loopback controller.
const crypto = require('crypto');
/**
* Canonicalize headers by removing or rejecting values containing \r or \n.
* Returns a sanitized object and a canonical string for signing.
*/
function canonicalizeForHmac(headers, body) {
const normalizedHeaders = {};
const headerLines = [];
for (const [key, value] of Object.entries(headers)) {
if (typeof value !== 'string' || /[\r\n]/.test(value)) {
throw new Error('Invalid header value: contains line break');
}
normalizedHeaders[key] = value;
headerLines.push(`${key.toLowerCase()}:${value}`);
}
const canonical = headerLines.join('\n') + '\n' + body;
return { normalizedHeaders, canonical };
}
/**
* Sign payload using Hmac-SHA256 with a fixed secret.
*/
function signPayload(headers, body, secret) {
const { canonical } = canonicalizeForHmac(headers, body);
const hmac = crypto.createHmac('sha256', secret);
hmac.update(canonical);
return hmac.digest('hex');
}
/**
* Verify incoming request Hmac signature after sanitization.
*/
function verifyRequest(req, secret) {
const supplied = req.headers['x-signature'];
if (!supplied) return false;
const body = typeof req.body === 'string' ? req.body : JSON.stringify(req.body);
const { canonical } = canonicalizeForHmac(req.headers, body);
const expected = crypto.createHmac('sha256', secret).update(canonical).digest('hex');
// Use timing-safe compare to avoid side channels
return crypto.timingSafeEqual(Buffer.from(supplied), Buffer.from(expected));
}
// Example usage in a Loopback controller method
exports.processWebhook = async function(context) {
const rawBody = context.req.rawBody; // ensure raw string available
const headers = { 'x-timestamp': context.req.headers['x-timestamp'], 'x-event-type': context.req.headers['x-event-type'] };
const secret = process.env.HMAC_SECRET;
if (!verifyRequest(context.req, secret)) {
const err = new Error('Invalid signature');
err.statusCode = 401;
throw err;
}
// Continue processing verified webhook
};
Key remediation practices:
- Reject or sanitize any header values containing CR or LF characters before they are included in the canonical string used for Hmac computation.
- Normalize line endings to \n when constructing the canonical form, and reject inputs that contain \r to avoid ambiguity across platforms.
- Apply timing-safe comparison (e.g., crypto.timingSafeEqual) when verifying Hmac signatures to prevent side-channel leaks.
- For webhook verification, ensure the raw body is preserved exactly as received before canonicalization, avoiding frameworks that coerce or reformat the payload before signature validation.
Frequently Asked Questions
How can I test if my Loopback API is vulnerable to Crlf Injection in Hmac-signed requests?
Does middleBrick detect Crlf Injection patterns in Loopback APIs with Hmac Signatures?
middlebrick scan <url>, or add the GitHub Action to fail builds if a security score drops below your chosen threshold.