Injection Flaws in Express with Hmac Signatures
Injection Flaws in Express with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Injection flaws in Express applications that rely on Hmac signatures can arise when input validation is incomplete and signature verification is applied inconsistently. An Hmac signature is typically computed over a canonical representation of a request’s payload or query parameters using a shared secret. If the server normalizes or parses incoming data differently than the client before signing, an attacker can supply carefully crafted payloads that bypass signature checks or alter the interpreted command. This mismatch can occur when the server deserializes JSON, form data, or URL-encoded parameters before verifying the Hmac, allowing injection of extra fields, nested objects, or encoded characters that change the effective input without invalidating the signature.
Express-specific risks include inconsistent body-parser configurations and middleware ordering. For example, using express.json() before signature verification may cause property reordering or whitespace handling differences that break canonicalization. An attacker can exploit this by injecting duplicate keys, polymorphic types, or encoded Unicode to achieve injection—such as escalating privileges by injecting an isAdmin field or modifying an amount parameter—while the Hmac still validates because the server’s normalization diverges from the client’s construction logic.
Another injection vector arises from query parameters and URL paths. If an Hmac is computed only over selected parameters but the server also uses untrusted query values in database or command operations, an attacker can inject malicious values that affect behavior even when the signature is valid. A common pattern is signing a subset of fields (e.g., userId and action) while neglecting to bind the signature to the full request context. This enables IDOR or privilege escalation through parameter tampering. SSRF and command injection can occur if the server uses unchecked input from the request to form external calls, despite a valid Hmac proving only that the known parameters have not been altered.
LLM/AI Security checks in middleBrick can detect scenarios where untrusted input reaches LLM endpoints constructed from request data, exposing prompt injection or data exfiltration risks when Hmac-protected APIs are used to build dynamic prompts. Meanwhile, BFLA and Property Authorization checks highlight cases where Hmac scope is too narrow, allowing a valid signature to be reused across different resources or actions, effectively bypassing intended authorization boundaries.
Hmac Signatures-Specific Remediation in Express — concrete code fixes
Remediation centers on canonicalization, strict validation, and ensuring the Hmac covers all data that influences server-side behavior. Use a consistent body parser configuration and verify the signature before any deserialization that could alter the payload. Prefer hashing the raw request body for JSON APIs, and for non-JSON payloads, enforce a strict parsing routine that does not modify the original bytes used for signing.
The following example shows a secure Express pattern using crypto.createHmac. The server computes the Hmac over the raw JSON string and compares it with a header value using timing-safe comparison. The body is parsed only after successful verification, preventing injection via nested objects or coercion.
const express = require('express');
const crypto = require('crypto');
const app = express();
// Secret stored securely, e.g., from environment
const SHARED_SECRET = process.env.HMAC_SECRET;
// Middleware that preserves raw body for Hmac verification
app.use(express.json({ type: 'application/json' }));
function verifyHmac(req, res, next) {
const signature = req.headers['x-hmac-signature'];
if (!signature) {
return res.status(401).json({ error: 'Missing signature' });
}
// Use raw body buffer to ensure canonical representation
const rawBody = req.rawBody || JSON.stringify(req.body);
const hmac = crypto.createHmac('sha256', SHARED_SECRET);
const computed = hmac.update(rawBody).digest('hex');
// Timing-safe compare
const isValid = crypto.timingSafeEqual(
Buffer.from(computed),
Buffer.from(signature)
);
if (!isValid) {
return res.status(403).json({ error: 'Invalid signature' });
}
next();
}
// Apply verification before any route that uses Hmac-signed input
app.post('/transfer', verifyHmac, (req, res) => {
const { userId, amount, currency } = req.body;
// Perform authorization and business logic after verification
if (typeof userId !== 'number' || typeof amount !== 'number' || amount <= 0) {
return res.status(400).json({ error: 'Invalid payload' });
}
// Execute transfer logic…
res.json({ status: 'ok' });
});
For form-encoded or URL-encoded payloads, compute the Hmac over the serialized string in a consistent order. Avoid including metadata or extra parameters that are not part of the signature scope. The following example demonstrates signing and verification for URL-encoded data with sorted keys to ensure canonical ordering:
const querystring = require('querystring');
const crypto = require('crypto');
function buildSignedQuery(params, secret) {
const sortedKeys = Object.keys(params).sort();
const canonical = sortedKeys.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`).join('&');
const hmac = crypto.createHmac('sha256', secret);
const signature = hmac.update(canonical).digest('hex');
return { query: `${canonical}&hmac=${encodeURIComponent(signature)}`, signature };
}
// Verification on the server
function verifySortedQuery(req, res, next) {
const params = { ...req.query };
delete params.hmac;
const sortedKeys = Object.keys(params).sort();
const canonical = sortedKeys.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`).join('&');
const hmac = crypto.createHmac('sha256', SHARED_SECRET);
const expected = hmac.update(canonical).digest('hex');
const provided = req.query.hmac;
const isValid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided));
if (!isValid) {
return res.status(403).json({ error: 'Invalid query signature' });
}
next();
}
Additionally, scope Hmac signatures narrowly and bind them to the intended action and resource. Include a version or timestamp to prevent replay attacks and ensure each signature is used only within its validity window. middleBrick’s BFLA and Property Authorization checks can help identify when a valid Hmac is reused across endpoints or permissions, reducing the impact of injection attempts that rely on signature portability.