HIGH xpath injectionexpresshmac signatures

Xpath Injection in Express with Hmac Signatures

Xpath Injection in Express with Hmac Signatures — how this specific combination creates or exposes the vulnerability

XPath Injection occurs when untrusted data is concatenated into an XPath expression without proper sanitization or parameterization, allowing an attacker to alter query logic. In an Express service that uses XML documents and relies on XPath for querying, this can expose sensitive data or bypass authorization checks. When HMAC signatures are used to authenticate requests, a common assumption is that the signature protects the integrity of the entire request, including any query parameters or body fields used to build XPath expressions. However, if the server constructs XPath strings by directly interpolating values from the request body or query parameters before signature verification, an attacker can inject malicious XPath syntax that changes the query outcome while still presenting a valid HMAC.

Consider an endpoint /user/profile that accepts POST data containing an email field and an HMAC signature in a header. The server computes the HMAC over the raw payload, verifies the signature, and then builds an XPath like //user[email='INPUT'] by inserting the email value directly. Because the signature covers the original input, the attacker can supply an email such as ' or 1=1 or 'a'='a, which terminates the string and appends additional conditions. The resulting XPath might become //user[email='' or 1=1 or 'a'='a'], returning all user nodes. The HMAC remains valid because the attacker’s input is part of the signed payload, demonstrating that signature verification does not prevent injection if the application logic treats signed input as safe for XPath construction.

This pattern is particularly risky when the signed payload includes metadata that influences document selection, such as identifiers used in XPath axes or predicates. An attacker who can control even a single parameter can manipulate node selection, bypass filters, or extract data through error-based techniques that rely on XPath evaluation errors. Because XPath lacks parameterized query APIs in many JavaScript libraries, developers must explicitly escape quotes and normalize inputs. In Express, middleware that validates signatures before parsing and normalizing user-supplied values for XPath use creates a false sense of security. The combination of Hmac Signatures for integrity and unchecked XPath construction shifts the threat from authentication bypass to data exposure and privilege escalation within the XML data layer.

Hmac Signatures-Specific Remediation in Express — concrete code fixes

To mitigate XPath Injection while using Hmac Signatures in Express, ensure that user-controlled values are never directly interpolated into XPath expressions. Instead, use parameterized approaches or strict validation, and apply escaping for string literals within XPath. Treat the HMAC as a verification layer, not a sanitization layer. Below are concrete patterns and code examples for secure handling.

1. Validate and sanitize before signature verification

Normalize and validate input before including it in XPath logic. For email-based queries, enforce a strict email format and escape single quotes by doubling them, which is the standard escape mechanism in XPath 1.0.

const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');

const app = express();
app.use(bodyParser.json({ type: 'application/json' }));

function computeHmac(payload, secret) {
  return crypto.createHmac('sha256', secret).update(payload).digest('hex');
}

function verifyHmac(req, secret) {
  const received = req.get('X-API-Signature');
  if (!received) return false;
  const expected = computeHmac(JSON.stringify(req.body), secret);
  return crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}

function escapeXpathString(value) {
  // Replace single quote with two single quotes for XPath string literal
  return value.replace(/'/g, "''");
}

const SECRET = process.env.HMAC_SECRET;

app.post('/user/profile', (req, res) => {
  if (!verifyHmac(req, SECRET)) {
    return res.status(401).json({ error: 'invalid signature' });
  }

  const email = req.body.email;
  if (typeof email !== 'string' || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
    return res.status(400).json({ error: 'invalid email' });
  }

  const safeEmail = escapeXpathString(email);
  const xpath = `//user[email='${safeEmail}']`;

  // Use xpath with a proper library that supports parameterized queries if available
  // Example using an imaginary xpath library:
  // const result = xml.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);

  res.json({ xpath, status: 'safe' });
});

2. Use a library that supports parameterized XPath or switch to XQuery parameters

If your XPath library supports binding variables, prefer that over string concatenation. For environments where parameterized XPath is unavailable, rigorously escape and validate. Below is an example assuming a library that accepts a map of variables.

const xpath = require('xpath'); // hypothetical library supporting variables
const dom = require('xmldom').DOMParser;

app.post('/user/search', (req, res) => {
  if (!verifyHmac(req, SECRET)) {
    return res.status(401).json({ error: 'invalid signature' });
  }

  const email = req.body.email;
  if (typeof email !== 'string' || !/^[\w-\.]+@([\w-]+\.)+[\w-]{2,}$/.test(email)) {
    return res.status(400).json({ error: 'invalid email' });
  }

  // Using variable binding if supported
  const doc = new dom().parseFromString(xmlData);
  const selectNode = xpath.select(`//user[email=$email]`, doc, { email });

  res.json({ nodes: selectNode.length });
});

3. Defense in depth: schema validation and allowlists

Complement input handling with JSON Schema validation to restrict the structure and types of incoming data. This reduces the attack surface available for XPath manipulation even before HMAC verification.

const Ajv = require('ajv');
const ajv = new Ajv();

const profileSchema = {
  type: 'object',
  required: ['email'],
  properties: {
    email: { type: 'string', format: 'email' }
  },
  additionalProperties: false
};

const validateProfile = ajv.compile(profileSchema);

app.post('/user/profile', (req, res) => {
  if (!validateProfile(req.body)) {
    return res.status(400).json({ errors: validateProfile.errors });
  }
  if (!verifyHmac(req, SECRET)) {
    return res.status(401).json({ error: 'invalid signature' });
  }

  const safeEmail = escapeXpathString(req.body.email);
  const xpath = `//user[email='${safeEmail}']`;
  res.json({ xpath });
});

Frequently Asked Questions

Does HMAC verification prevent XPath Injection in Express?
No. Hmac Signatures ensure request integrity but do not sanitize user input. If you interpolate signed values directly into XPath expressions, attackers can still inject malicious syntax that alters query results while keeping the signature valid.
What is the safest way to handle user input for XPath in Express with Hmac Signatures?
Validate input strictly (e.g., email format), escape string literals for XPath (double single quotes), and avoid string concatenation by using parameterized XPath if your library supports it. Treat HMAC as an authentication check, not an authorization or sanitization mechanism.