Preventing Replay Attacks
Introduction
Replay attacks occur when an attacker captures a legitimate request and replays it to gain unauthorized access. APIs that rely on static tokens or signatures without freshness checks are especially vulnerable.
Common Replay Scenarios
- Captured bearer tokens reused from logs or intercepted traffic.
- Signed requests replayed within the signature validity window.
- Webhook endpoints with predictable payload signatures.
Defense in Depth
Effective replay defense combines multiple layers.
- Use short-lived access tokens and rotate refresh tokens.
- Enforce request timestamps with narrow acceptance windows.
- Require unique nonces and store them to prevent reuse.
- Bind tokens to TLS channels or device identities.
Node.js Example: HMAC Requests with Nonce Validation
The following Node.js example validates a timestamped HMAC and tracks nonces to prevent replay.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import crypto from "crypto";
const NONCE_TTL_MS = 5 * 60 * 1000;
const nonceCache = new Map();
// Evict expired nonces periodically to prevent memory leaks
setInterval(() => {
const now = Date.now();
for (const [key, ts] of nonceCache) {
if (now - ts > NONCE_TTL_MS) {
nonceCache.delete(key);
}
}
}, NONCE_TTL_MS);
function validateRequest({ body, nonce, timestamp, signature, secret }) {
const now = Date.now();
const age = Math.abs(now - Number(timestamp));
if (age > NONCE_TTL_MS) {
throw new Error("Timestamp outside acceptable window");
}
if (nonceCache.has(nonce)) {
throw new Error("Nonce already used");
}
const payload = `${timestamp}.${nonce}.${body}`;
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
throw new Error("Invalid signature");
}
nonceCache.set(nonce, now);
}
Handling Clock Skew
- Allow a small skew window for distributed clients.
- Reject requests without timestamps rather than defaulting to permissive behavior.
- Evict old nonces aggressively to limit memory growth.
Conclusion
Replay prevention is not a single feature. Combine time-bound tokens, nonce tracking, and cryptographic signatures to ensure every request is unique and time-bound.
This post is licensed under CC BY 4.0 by the author.