HIGH auth bypassexpresshmac signatures

Auth Bypass in Express with Hmac Signatures

Auth Bypass in Express with Hmac Signatures — how this specific combination creates or exposes the vulnerability

HMAC-based authentication in Express relies on a shared secret to sign requests, typically by hashing selected request properties (method, path, selected headers, and body fields) and including the signature in a header such as x-api-signature. When implemented incorrectly, an attacker can bypass authentication by preventing the server from computing the same signature as the client, even when credentials are not leaked.

One common pattern that leads to Auth Bypass is inconsistent signature inputs between client and server. If the client signs a canonical representation of the request but the server reconstructs the signature using a different ordering of parameters, different serialization of JSON (e.g., property order or whitespace), or a different set of signed headers, the signatures will not match. In some designs, the server may fall back to an unsigned or default verification path when signature reconstruction fails, effectively disabling authentication for malformed or unexpected requests. This can occur when error handling for signature verification is too permissive or when middleware tolerates missing signature headers in development-like configurations.

Another vulnerability vector involves the use of vulnerable body parsing or content-type handling. For example, if the server uses express.json() and also relies on raw body for signature verification but does not handle body hashing consistently, an attacker can send requests with mismatched body representations (e.g., duplicate keys in JSON, nested object ordering differences) that produce different hashes. If the server compares signatures using a non-constant-time method or does not validate the presence of the signature header, it may inadvertently accept unsigned or tampered requests. Inconsistent handling of content types, such as accepting both application/json and form-encoded payloads without a unified signing strategy, can also introduce bypass opportunities.

Path and method handling further influence risk. If route definitions are ambiguous (e.g., overlapping routes with different parameter patterns) or if the signing process does not canonicalize the request path (including trailing slashes or case-sensitive differences), an attacker may craft a request that matches an unintended route and evades the signature check. Similarly, omitting critical headers (such as a timestamp or nonce) from the signed payload can enable replay attacks where a captured request is reused, because the server may not detect replay without strict validation of freshness indicators.

Middleware ordering is another subtle factor. If signature verification middleware is placed after parsers that modify the request body or headers, the values used for verification may differ from what the client signed. For instance, if a body parser mutates the request object before the signature step, the server’s computed hash diverges from the client’s hash, and a permissive implementation might skip verification or fall back to an insecure default. Proper sequencing and immutable handling of raw data are essential to ensure that what is verified matches what was originally signed.

These issues manifest in real-world findings such as Weak Verification Logic and BOLA/IDOR patterns identified in scans, where inconsistent or missing checks allow unauthorized access. Because HMAC schemes depend on exact reconstruction of inputs, any divergence between client and server logic can neutralize the intended protection, making rigorous canonicalization and strict verification critical to preventing Auth Bypass in Express services that use Hmac Signatures.

Hmac Signatures-Specific Remediation in Express — concrete code fixes

To remediate Hmac Signature issues in Express, align client and server canonicalization precisely and enforce strict verification. Use a deterministic serialization for the data to be signed, explicitly include the request method, path, selected headers, and a stable body representation, and apply signature checks before any parsing that could mutate the request.

const crypto = require('crypto');
const express = require('express');
const app = express();

// Raw body middleware to preserve bytes for signature verification
app.use(express.json({ verify: (req, res, buf) => { req.rawBody = buf; } }));

function computeSignature(rawBody, method, path, headers, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  const canonicalHeaders = ['x-date', 'content-type'].map(k => k.toLowerCase()).sort().map(k => `${k}:${headers[k]}`).join('\n');
  const payload = [
    method.toUpperCase(),
    path,
    headers['x-date'] || '',
    canonicalHeaders,
    rawBody.toString('utf8') || ''
  ].join('\n');
  return hmac.update(payload).digest('hex');
}

function verifySignature(req, res, next) {
  const signature = req.get('x-api-signature');
  const secret = process.env.SHARED_SECRET;
  if (!signature || !secret) { return res.status(401).send('Missing signature'); }
  const expected = computeSignature(req.rawBody, req.method, req.path, req.headers, secret);
  const safeCompare = crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
  if (!safeCompare) { return res.status(403).send('Invalid signature'); }
  next();
}

app.use(verifySignature);

app.get('/resource', (req, res) => {
  res.json({ ok: true });
});

app.listen(3000, () => console.log('Server running on port 3000'));

This example demonstrates canonicalization by sorting selected headers, including an x-date timestamp to prevent replays, and using timingSafeEqual to avoid timing attacks. Ensure both client and server use identical header lists, path formats (including or excluding trailing slashes), and body representations (e.g., no extra whitespace or reordered keys). For production, rotate SHARED_SECRET securely and validate x-date freshness to mitigate replay.

When integrating with scans, combine these practices with the platform’s checks for Weak Verification Logic and BOLA/IDOR. The CLI (middlebrick scan <url>) can validate your endpoints, while the GitHub Action adds API security checks to your CI/CD pipeline and can fail builds if risk scores drop below your chosen threshold. For continuous coverage across versions, the Pro plan supports scheduled scans with alerts, and the MCP Server lets you scan APIs directly from your AI coding assistant within the development flow.

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

How can I ensure my HMAC signature implementation is canonical on both client and server?
Define a strict canonicalization specification that includes the HTTP method, the request path exactly as sent (including trailing slash rules), a sorted list of signed headers with their values, and a stable body representation (e.g., raw bytes for JSON without extra whitespace or with a canonical serialization like sorted keys). Use the same hashing algorithm (e.g., SHA-256) and encoding (hex) on both sides, and verify signatures before any body parsing that could mutate the request.
What should I do if signature verification fails due to minor differences like header order or whitespace?
Treat any verification failure as a rejection; do not fall back to unsigned or default access. Enforce strict canonicalization on both client and server so that minor differences do not occur. If you must tolerate some flexibility for legacy reasons, explicitly log and audit these events, but prefer to reject and require the client to resend a correctly formed request.