Ssrf Server Side in Express with Hmac Signatures
Ssrf Server Side in Express with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Server-side request forgery (SSRF) in an Express service that uses HMAC signatures can arise when a client-supplied URL is used both to drive internal HTTP requests and to validate an HMAC-based integrity check. If the signature is computed over a subset of parameters (e.g., a URL path or a selected set of query keys) but the runtime logic uses a different, broader set to decide where to forward, an attacker can supply a URL that passes signature verification yet directs the server to an internal or unexpected endpoint. Consider an Express route that expects a resource ID and an HMAC over that ID; if the server then appends the ID to a base URL chosen server-side and forwards the request, the attacker-supplied ID can be crafted to change the effective target (e.g., injecting metadata service IPs like 169.254.169.254). The HMAC does not protect the server’s outbound request destination; it only validates the client input under the assumptions made at signing time. Common patterns that exacerbate this include failing to normalize URLs before signing, allowing open redirects, or trusting user input for host resolution without strict allowlists. Because the scan tests unauthenticated attack surfaces, an SSRF vector reachable without credentials can be discovered through input validation and SSRF-specific checks, highlighting how a seemingly integrity-protected endpoint can still lead to unintended internal network interactions.
Hmac Signatures-Specific Remediation in Express — concrete code fixes
Remediation centers on ensuring the data used to compute the HMAC and the data used to make runtime routing decisions are bound together and validated against a strict allowlist. Do not rely on the HMAC alone to guarantee that a forwarded request targets an expected destination; instead, decouple the identifier from the target host and path, and enforce allowlists for protocols and hosts. Below are two concrete Express examples: a vulnerable pattern and a hardened pattern.
Vulnerable pattern to avoid
const crypto = require('crypto');
const axios = require('axios');
const express = require('express');
const app = express();
app.get('/resource', (req, res) => {
const { id, signature } = req.query;
const expected = crypto.createHmac('sha256', 'secret').update(id).digest('hex');
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
// Dangerous: attacker-controlled `id` influences the target URL
const target = `https://api.partner.com/data/${id}`;
axios.get(target).then(r => res.send(r.data)).catch(e => res.status(500).send('error'));
});
Hardened remediation with allowlist and fixed target
const crypto = require('crypto');
const axios = require('axios');
const express = require('express');
const app = express();
const ALLOWED_KINDS = new Set(['profile', 'settings', 'metrics']);
const FIXED_BASE = 'https://api.partner.com/v1';
function signPayload(payload) {
return crypto.createHmac('sha256', 'secret').update(payload).digest('hex');
}
app.get('/resource', (req, res) => {
const { kind, id, signature } = req.query;
// 1) Validate kind against an allowlist
if (!ALLOWED_KINDS.has(kind)) {
return res.status(400).send('Invalid kind');
}
// 2) Validate ID format strictly (e.g., integer or UUID pattern)
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id)) {
return res.status(400).send('Invalid id');
}
// 3) Recompute signature over the same canonical payload used at runtime
const payload = `${kind}:${id}`;
const expected = signPayload(payload);
if (signature !== expected) {
return res.status(401).send('Invalid signature');
}
// 4) Build the target from fixed base + kind + id; do not trust host from client
const target = `${FIXED_BASE}/${kind}/${id}`;
return axios.get(target).then(r => res.send(r.data)).catch(e => res.status(500).send('error'));
});
Key practices illustrated:
- Sign a canonical representation that includes both the kind and the ID, so tampering with either breaks verification.
- Use an allowlist for
kindto prevent protocol smuggling or host override. - Validate
idwith a strict pattern (e.g., UUID) to avoid path traversal or injection. - Construct the outbound URL from a fixed base and validated components, never by concatenating user input directly into a URL provided by the client.
- Keep the HMAC secret out of source control and consider rotating it via environment variables with restricted access.
Even when HMACs are used, SSRF risks persist if the server’s outbound destination is derived from unchecked user input. The scans available through the middleBrick ecosystem can help detect such misconfigurations by testing unauthenticated attack surfaces. If you want to automate verification across endpoints, the CLI tool can be integrated into scripts with middlebrick scan <url>, and the GitHub Action can enforce a minimum security score in CI/CD pipelines.