Replay Attack in Fiber with Hmac Signatures
Replay Attack in Fiber with Hmac Signatures — how this specific combination creates or exposes the vulnerability
A replay attack in the Fiber HTTP framework when Hmac Signatures are used for request authentication occurs when an attacker captures a valid request—complete with signature—and re-sends it to the server to reproduce the original effect. Because Hmac Signatures typically cover the HTTP method, path, and selected headers or a canonical body, a replayed request can still produce a valid signature if the server does not enforce additional protections. The vulnerability is not in the Hmac algorithm itself, but in the lack of a per-request or short-lived nonce, timestamp, or one-time-use metadata in the verification logic.
Consider a payment endpoint that uses an Hmac-SHA256 signature of the request body and a timestamp header. An attacker records a legitimate charge request and replays it within the allowed timestamp window. If the server only validates the signature and the timestamp format without ensuring the timestamp is recent enough or that the same timestamp+nonce combination has not been seen, the server may process the duplicate transaction. This maps to the OWASP API Top 10 API1:2023-Broken Object Level Authorization and API7:2023-API Security Misconfiguration when replay leads to unauthorized operations or data exposure.
In a black-box scan, middleBrick tests for this class of issue by submitting the same request multiple times and checking whether the server accepts repeated nonces or timestamps, and by probing for missing idempotency controls. The scanner also inspects whether the API specification (OpenAPI 2.0/3.0/3.1) documents replay protections such as a X-Request-Id header or a nonce requirement, and whether runtime behavior aligns with the declared security expectations.
Hmac Signatures-Specific Remediation in Fiber — concrete code fixes
To mitigate replay in Fiber with Hmac Signatures, include a server-side nonce store and strict timestamp validation, and ensure the signature covers a per-request unique value. Below are concrete, working examples using the crypto/hmac package in a Fiber handler.
Example 1: Signature with timestamp and server-side nonce check
const { app, ctx } = require('fiber');
const crypto = require('crypto');
const SECRET = process.env.HMAC_SECRET; // keep this server-side
const usedNonces = new Set(); // in production, use a fast TTL cache (e.g., redix or a LRU map)
const MAX_TIMESTAMP_SKEW = 300; // 5 minutes in seconds
function verifyHmac(req, res, next) {
const timestamp = req.get('X-Timestamp');
const nonce = req.get('X-Nonce');
const received = req.get('X-Signature');
if (!timestamp || !nonce || !received) {
return res.status(400).send({ error: 'missing_auth_headers' });
}
// Reject stale or far-future requests to prevent replay and time manipulation
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - Number(timestamp)) > MAX_TIMESTAMP_SKEW) {
return res.status(400).send({ error: 'timestamp_skew' });
}
// Reject reused nonces to prevent replay
if (usedNonces.has(nonce)) {
return res.status(401).send({ error: 'replay_rejected' });
}
const payload = `${timestamp}.${nonce}.${req.body}`;
const expected = crypto.createHmac('sha256', SECRET).update(payload).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) {
return res.status(401).send({ error: 'invalid_signature' });
}
// Store nonce with a TTL; here we simplify with a periodic cleanup
usedNonces.add(nonce);
if (usedNonces.size > 10000) {
// naive cleanup; use a TTL cache in production
usedNonces.clear();
}
next();
}
const app = new (require('fiber'))();
app.post('/payment', verifyHmac, (req, res) => {
// process payment — safe from replay within time window and nonce uniqueness
res.send({ ok: true });
});
app.listen(3000);
Example 2: Include an idempotency key in the signed string
const { app } = require('fiber');
const crypto = require('crypto');
const SECRET = process.env.HMAC_SECRET;
const processedKeys = new Set(); // use a persistent store in production
app.post('/transfer', (req, res) => {
const idempotencyKey = req.get('X-Idempotency-Key');
const timestamp = req.get('X-Timestamp');
const receivedSig = req.get('X-Signature');
if (!idempotencyKey || !timestamp || !receivedSig) {
return res.status(400).send({ error: 'missing_fields' });
}
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - Number(timestamp)) > 300) {
return res.status(400).send({ error: 'invalid_timestamp' });
}
// Reject duplicate idempotency keys
if (processedKeys.has(idempotencyKey)) {
return res.status(409).send({ error: 'duplicate_request' });
}
const payload = `${timestamp}.${idempotencyKey}.${JSON.stringify(req.body)}`;
const expected = crypto.createHmac('sha256', SECRET).update(payload).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(receivedSig), Buffer.from(expected))) {
return res.status(401).send({ error: 'bad_signature' });
}
processedKeys.add(idempotencyKey);
// actual business logic here
res.send({ ok: true });
});
Best-practice summary
- Include a nonce or idempotency key in the signed payload and enforce uniqueness server-side.
- Validate a tight timestamp window to bound the period during which a captured request is valid.
- Use
crypto.timingSafeEqualto prevent timing attacks on signature comparison. - Document the replay protections (nonce, timestamp, idempotency) in your OpenAPI spec so scanners and consumers can verify expectations.
By combining Hmac Signatures with per-request uniqueness and freshness checks, you reduce the risk of replay in Fiber applications to the benefit of authentication integrity.