HIGH open redirectfeathersjshmac signatures

Open Redirect in Feathersjs with Hmac Signatures

Open Redirect in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability

An Open Redirect in a Feathersjs application that uses Hmac Signatures can occur when a redirect URL is derived from user-controlled input and the Hmac protection is applied incompletely or after the redirect decision is made. Feathersjs is a framework for building REST and real-time APIs; it does not enforce a canonical redirect policy by default. If an endpoint accepts a redirect_to or similar parameter and uses it without strict validation, an attacker can supply a malicious external URL.

When Hmac Signatures are used—typically to sign query parameters or a payload to ensure integrity—the vulnerability arises if the signature is computed over the data that excludes the redirect target, or if the signature is verified after the redirect is issued. For example, an endpoint might sign the user identifier or a timestamp but fail to include the redirect URL in the signed string. An attacker can then keep the signature valid for a benign parameter while changing the redirect target to a phishing site. Because the signature validates only part of the request, the server trusts the request and proceeds with the redirect to the attacker-controlled location.

Consider a pattern where a client receives a signed token that authorizes a redirect. If the server generates the signature like hmac = createHmac('sha256', secret).update(userId).digest('hex') but later performs res.redirect(req.query.redirect_to) without re-including redirect_to in the signed data, the signature does not protect the redirect destination. An attacker can provide https://evil.com/steal as redirect_to while the signature still appears valid because it was computed only on userId. This leads to an authenticated phishing flow where users are redirected after a legitimate-looking signature check.

Another scenario involves query parameters that are signed to prevent tampering, but the application parses the redirect URL from a non-signed parameter. Even if the Hmac is verified, if the redirect logic does not recompute the signature over the final redirect target—or if it allows open redirects via relative paths that resolve to external domains—an attacker can supply a URL that bypasses intended navigation boundaries. Feathersjs services often expose such endpoints when authorization rules focus on resource ownership (e.g., BOLA/IDOR) and neglect strict referer or host validation alongside signature checks.

In summary, the combination of Feathersjs routing and Hmac Signatures becomes risky when the redirect target is not part of the signed payload or when the signature verification step does not enforce strict allowlists for redirect destinations. Without these safeguards, an attacker can leverage a valid Hmac to perform an Open Redirect, potentially leading to credential harvesting or malware distribution through the trusted application domain.

Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes

To remediate Open Redirect when using Hmac Signatures in Feathersjs, ensure the redirect target is either omitted from client-controlled input or is included in the signed data and strictly validated. Below are concrete code examples demonstrating secure patterns.

Example 1: Sign the redirect target and validate host allowlist

const crypto = require('crypto');

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

function verifyAndRedirect(req, res, secret, allowedHosts) {
  const receivedSignature = req.query.signature;
  const payload = {
    userId: req.query.userId,
    redirectTo: req.query.redirect_to
  };
  const expectedSignature = signPayload(payload, secret);

  if (receivedSignature !== expectedSignature) {
    throw new Error('Invalid signature');
  }

  const { redirectTo } = payload;
  try {
    const url = new URL(redirectTo);
    if (!allowedHosts.includes(url.hostname)) {
      throw new Error('Redirect to disallowed host');
    }
    res.redirect(redirectTo);
  } catch (err) {
    // Fallback to a safe internal route
    res.redirect('/dashboard');
  }
}

// Usage in a Feathers hook
module.exports = function () {
  return async context => {
    const { app, params } = context;
    // Assume params contains userId and redirect_to from a pre-validated source
    const secret = process.env.HMAC_SECRET;
    const allowedHosts = ['app.yourdomain.com', 'dashboard.yourdomain.com'];
    verifyAndRedirect(params.query, context.result, secret, allowedHosts);
    return context;
  };
};

Example 2: Omit redirect from signed data and use a strict internal map

const crypto = require('crypto');

function createSignedRedirect(userId, secret, path) {
  const payload = { userId, path };
  const signature = crypto.createHmac('sha256', secret).update(JSON.stringify(payload)).digest('hex');
  return `/auth/complete?signature=${signature}&userId=${encodeURIComponent(userId)}&path=${encodeURIComponent(path)}`;
}

function validateSignedRedirect(req, secret) {
  const { signature, userId, path } = req.query;
  const payload = { userId, path };
  const expected = crypto.createHmac('sha256', secret).update(JSON.stringify(payload)).digest('hex');
  if (signature !== expected) {
    throw new Error('Invalid signature');
  }
  // Only allow known safe paths to prevent open redirects
  const safePaths = new Set(['/dashboard', '/profile', '/settings']);
  if (!safePaths.has(path)) {
    throw new Error('Invalid path');
  }
  return path;
}

// Feathers service hook example
module.exports = function (options = {}) {
  return async context => {
    const secret = process.env.HMAC_SECRET;
    const safePath = validateSignedRedirect(context.params.query, secret);
    // Proceed with internal navigation; do not use untrusted redirect_to
    context.result = { url: `/internal/${safePath}` };
    return context;
  };
};

In both examples, the critical principle is to include the redirect target in the Hmac computation if it is necessary for the workflow, and to enforce a strict allowlist of hostnames or paths. Never compute the signature only on identifiers like user IDs or timestamps while separately trusting an external redirect URL. Additionally, prefer internal redirects or pre-approved path mappings over raw user-supplied URLs. These patterns ensure that the Hmac Signature protects the entire request, including any navigation instructions, thereby mitigating Open Redirect in Feathersjs applications.

Frequently Asked Questions

Can an attacker bypass Hmac Signatures by manipulating query parameters in a Feathersjs Open Redirect?
Yes, if the redirect URL is not included in the signed string or if the signature is verified after the redirect decision. Always sign the redirect target or remove it from user-controlled input and use an internal allowlist.
Does including the redirect URL in the Hmac Signature fully prevent Open Redirect in Feathersjs?
It significantly reduces risk, but you must also validate the resolved host against an allowlist and avoid forwarding to external domains. Combine signature checks with strict host/path validation for robust protection.