Replay Attack with Hmac Signatures
How Replay Attack Manifests in Hmac Signatures
HMAC signatures are a common mechanism for verifying message integrity and authenticity in API requests, using a shared secret to generate a cryptographic hash. However, an HMAC signature alone does not guarantee that a request is fresh or unique. A replay attack occurs when an attacker intercepts a valid, signed request and retransmits it to the server, which accepts it as legitimate because the HMAC validation passes. This specific vulnerability arises in HMAC-secured APIs when the signature is computed over static or predictable data without including a nonce (number used once) or timestamp, or when the server fails to enforce uniqueness or temporal constraints.
Consider a typical vulnerable pattern in Node.js/Express where an API endpoint validates an HMAC signature over the request body but does not incorporate any anti-replay elements:
const crypto = require('crypto');
app.post('/transfer', (req, res) => {
const secret = process.env.HMAC_SECRET;
const signature = req.headers['x-hmac-signature'];
const body = JSON.stringify(req.body);
const expectedHmac = crypto.createHmac('sha256', secret)
.update(body)
.digest('hex');
if (signature !== expectedHmac) {
return res.status(401).send('Invalid signature');
}
// Process transfer...
});An attacker who captures this request (e.g., via a compromised client or network sniffing) can replay it verbatim, causing the server to process the same transfer multiple times. The server has no way to distinguish the replayed request from the original because the body and signature remain valid. This is a direct violation of OWASP API Security Top 10 2023 item API2:2023 – Broken Authentication, as the authentication mechanism (HMAC) fails to prevent request duplication. Real-world incidents include CVE-2020-25540, where a similar flaw in a JWT-based system allowed replay due to missing nonce validation, demonstrating the criticality of this pattern across signature-based schemes.
Specific HMAC-related code paths where replay manifests include: (1) endpoints that sign only the request payload without including request metadata like timestamps, nonces, or method/path; (2) server-side verification that checks the HMAC but does not maintain a cache or store of recent nonces/timestamps to detect duplicates; (3) use of long-lived or static secrets without rotation, making captured signatures perpetually valid; and (4) absence of a freshness window (e.g., allowing signatures older than 5 minutes). The attack is particularly severe in financial or state-changing operations (e.g., payments, data deletion) where replay can cause direct monetary loss or data corruption.
Hmac Signatures-Specific Detection
Detecting replay vulnerabilities in HMAC-secured APIs requires verifying whether the server accepts identical signed requests more than once, or whether it accepts signatures that are cryptographically valid but stale. Manual testing involves capturing a legitimate request (e.g., with Burp Suite), then replaying it repeatedly to observe duplicate processing. However, automated scanning is essential for scale. middleBrick's Authentication and Input Validation checks specifically target this issue by:
- Submitting a test request to the endpoint and capturing the resulting HMAC signature (or observing if the endpoint requires one).
- Replaying the exact same request (including headers and body) multiple times to determine if the server processes it each time without rejection.
- Testing for the presence of anti-replay mechanisms: inspecting requests/responses for nonce or timestamp parameters, and attempting to modify these values to see if the server rejects reused or outdated values.
- Analyzing OpenAPI/Swagger specifications (if available) to identify operations that use
securitySchemeswithhmacorsignaturetypes, then cross-referencing runtime behavior.
For example, middleBrick might generate a report snippet like:
{
"finding": "Potential Replay Attack",
"severity": "high",
"endpoint": "POST /api/v1/transfer",
"evidence": "Same HMAC signature accepted on 3 consecutive replays",
"remediation": "Include a timestamp and nonce in the signed payload; reject requests with nonces seen within the last 5 minutes."
}You can run this detection yourself using middleBrick's tools. The CLI allows quick scans from the terminal:
middlebrick scan https://api.example.comFor CI/CD integration, the GitHub Action can be configured to fail a build if a replay vulnerability is detected in a staging API. The Web Dashboard provides a per-category breakdown under the Authentication section, highlighting replay issues with severity ratings and step-by-step remediation guidance. This ensures that even without deep manual testing, teams can identify whether their HMAC implementation lacks nonce/timestamp validation.
Hmac Signatures-Specific Remediation
Remediating replay attacks in HMAC signatures requires adding uniqueness and freshness to the signed data, and enforcing server-side checks. The standard approach is to include a nonce (a cryptographically random, unique value) and a timestamp within the data that is signed. The server then verifies that the nonce has not been used before (within a short time window) and that the timestamp is recent.
Here is a corrected implementation in Node.js/Express using the built-in crypto module. The client generates a nonce and timestamp, includes them in the request body, and signs the entire body (including nonce and timestamp). The server validates the HMAC, then checks the nonce against a cache (e.g., Redis or in-memory store) and the timestamp against a allowed window (e.g., 300 seconds).
// Client-side: generate request with nonce and timestamp
const crypto = require('crypto');
const nonce = crypto.randomBytes(16).toString('hex');
const timestamp = Math.floor(Date.now() / 1000); // seconds
const body = {
amount: 100,
to: 'user123',
nonce,
timestamp
};
const secret = 'shared-secret';
const signature = crypto.createHmac('sha256', secret)
.update(JSON.stringify(body))
.digest('hex');
// Send with headers
fetch('/transfer', {
method: 'POST',
headers: { 'X-HMAC-Signature': signature },
body: JSON.stringify(body)
});// Server-side: validate HMAC and replay protection
const crypto = require('crypto');
const usedNonces = new Set(); // In production, use Redis with TTL
app.post('/transfer', async (req, res) => {
const secret = process.env.HMAC_SECRET;
const signature = req.headers['x-hmac-signature'];
const body = req.body;
const expectedHmac = crypto.createHmac('sha256', secret)
.update(JSON.stringify(body))
.digest('hex');
if (signature !== expectedHmac) {
return res.status(401).send('Invalid signature');
}
// Check timestamp freshness (e.g., 5 minutes)
const now = Math.floor(Date.now() / 1000);
if (body.timestamp < now - 300) {
return res.status(401).send('Stale request');
}
// Check nonce uniqueness
if (usedNonces.has(body.nonce)) {
return res.status(401).send('Replay detected');
}
usedNonces.add(body.nonce);
// Optional: set a TTL to automatically remove old nonces
// Process transfer...
res.send('Success');
});Key remediation steps:
- Include a nonce and timestamp in the data that is signed. The nonce must be unpredictable (use
crypto.randomBytesor equivalent). - Enforce a short timestamp window (typically 1–5 minutes) to limit the replay window.
- Store used nonces with a short expiration (equal to the timestamp window) to prevent duplicates. Use a fast datastore like Redis with automatic key expiration for scalability.
- Sign all relevant request data, including HTTP method, path, and headers if they affect the operation, to prevent signature bypass via parameter tampering.
After implementing these fixes, rescan the API with middleBrick to confirm the replay issue is resolved. The Pro tier's continuous monitoring can be configured to alert if a future deployment reintroduces the vulnerability, and the GitHub Action can block merges that degrade the Authentication score. Note that middleBrick only detects and reports such issues; it does not modify your code—the remediation guidance provides the specific code patterns needed to fix the problem.