HIGH injection flawsexpresshmac signatures

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.

Frequently Asked Questions

Why does Hmac verification fail even when the signature looks correct?
This usually indicates a canonicalization mismatch: the client and server differ in how they serialize or encode data before signing. Ensure both sides use the same body representation (raw JSON string vs. parsed object) and the same parameter ordering, and avoid extra fields or whitespace differences that change the input without invalidating the signature.
Can Hmac signatures prevent all injection attacks in Express APIs?
No. Hmac signatures protect integrity of signed data but do not replace input validation, type checking, or output encoding. Injection flaws can still occur if the server uses unchecked values in database queries, command execution, or external calls. Combine Hmac verification with strict schema validation and parameterized operations.