HIGH session fixationexpresshmac signatures

Session Fixation in Express with Hmac Signatures

Session Fixation in Express with Hmac Signatures — how this specific combination creates or exposes the vulnerability

Session fixation occurs when an application forces a user to use a session identifier that the attacker knows or can set. In Express, developers sometimes rely on Hmac Signatures to bind session tokens or API requests to a particular context (e.g., a user ID or a nonce) without storing server-side session state. While Hmac Signatures can provide integrity and authenticity, they do not inherently prevent fixation if the signed value is supplied or accepted from an untrusted source.

Consider an Express route that creates an order and signs the order ID with an Hmac to prevent tampering:

const crypto = require('crypto');

const SECRET = process.env.HMAC_SECRET; // must be kept safe

function signOrder(orderId) {
  return crypto.createHmac('sha256', SECRET).update(orderId).digest('hex');
}

app.get('/order', (req, res) => {
  const { orderId } = req.query;
  const expected = signOrder(orderId);
  const received = req.query.signature;
  if (received !== expected) {
    return res.status(400).send('Invalid signature');
  }
  res.json({ orderId, signed: true });
});

If the orderId is chosen by the client and only validated via Hmac, an attacker can fixate a known order ID and trick a victim into using it. The server verifies the Hmac successfully because the attacker knows the signed value ahead of time, but the application logic treats that fixed ID as the victim’s transaction. This is a session fixation scenario where the signed parameter plays the role of the session identifier.

Another common pattern is signing JSON Web Tokens or session cookies with Hmac without ensuring a fresh token per authentication event:

const token = crypto.createHmac('sha256', SECRET)
  .update(userId + timestamp)
  .digest('hex');
res.cookie('session', token, { httpOnly: true });

If the token is not regenerated after login, an attacker who knows the token before authentication can maintain access after the victim logs in (fixation). Hmac Signatures ensure the token hasn’t been altered, but they do not prevent the use of a token that was previously known to the attacker.

The root cause is treating Hmac Signatures as a substitute for session initialization controls. The signature provides integrity, but the application must ensure that sensitive values (order IDs, user IDs, nonces) are generated server-side and never accepted directly from the client in a way that allows an attacker to predetermine them.

Hmac Signatures-Specific Remediation in Express — concrete code fixes

To mitigate session fixation when using Hmac Signatures in Express, ensure that signed values are generated server-side, are unguessable, and are rotated or bound to authentication events. Do not accept raw identifiers from the client for signing without strict validation and context checks.

1) Generate signed values server-side and avoid client-controlled inputs:

const crypto = require('crypto');

const SECRET = process.env.HMAC_SECRET;

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

// Server-generated order ID
app.get('/order', (req, res) => {
  const orderId = crypto.randomBytes(16).toString('hex');
  const signature = signOrder(orderId, SECRET);
  res.json({ orderId, signature });
});

2) Bind signatures to session context and rotate after authentication:

let sessionSecret = crypto.randomBytes(32).toString('hex');

function signWithSession(value) {
  return crypto.createHmac('sha256', sessionSecret).update(value).digest('hex');
}

// Rotate secret after login to prevent fixation
app.post('/login', (req, res) => {
  // validate credentials
  sessionSecret = crypto.randomBytes(32).toString('hex');
  const token = signWithSession(req.user.id);
  res.cookie('session', token, { httpOnly: true });
});

3) Include a nonce or timestamp and verify freshness to prevent replay and fixation:

function signOrder(orderId, nonce, secret) {
  return crypto.createHmac('sha256', secret).update(orderId).update(nonce).digest('hex');
}

app.get('/order', (req, res) => {
  const orderId = crypto.randomBytes(16).toString('hex');
  const nonce = Date.now().toString(); // or a server-side counter
  const signature = signOrder(orderId, nonce, SECRET);
  res.json({ orderId, nonce, signature });
});

4) Validate context on the server and reject client-supplied identifiers that should be generated by the server:

app.get('/confirm', (req, res) => {
  const { orderId, signature } = req.query;
  // Ensure orderId matches a server-side record for this user/session
  if (!isValidOrderForSession(orderId, req.session.id)) {
    return res.status(403).send('Forbidden');
  }
  const expected = signOrder(orderId, getSessionSecret(req.session.id));
  if (signature !== expected) {
    return res.status(400).send('Invalid signature');
  }
  res.json({ confirmed: true });
});

These practices ensure that Hmac Signatures are used to protect integrity and authenticity without enabling fixation. By generating sensitive values server-side, binding them to session context, and rotating secrets after authentication, you reduce the risk that a fixed signed value can be leveraged by an attacker.

Frequently Asked Questions

Can Hmac Signatures alone prevent session fixation in Express?
No. Hmac Signatures provide integrity and authenticity but do not prevent fixation if the signed value is chosen or influenced by the client. You must generate and bind signed values server-side and rotate them after authentication.
Should I accept order IDs or user IDs directly from the client for Hmac signing?
Avoid accepting raw identifiers from the client for signing. Generate server-side values (e.g., random order IDs) and only sign them. If you must sign client data, validate strict format, context, and freshness rigorously.