TL;DR
Timing attacks pull secret data out of how long an operation takes to run. The tell: === string comparison exits the moment it hits a mismatch, so a guess that matches more characters takes a hair longer to fail. That's the leak. Use a constant-time comparison like crypto.timingSafeEqual anywhere you're checking secrets, tokens, or passwords.
How Timing Attacks Work
// JavaScript === returns immediately on first difference
function checkApiKey(provided, actual) {
return provided === actual; // VULNERABLE!
}
// If actual = "secret123"
// "axxxxxxxx" - fails immediately (fast)
// "sxxxxxxxx" - first char matches, then fails (slightly slower)
// "sexxxxxxx" - two chars match (even slower)
// By measuring response times, attacker can guess one char at a time
What Can Be Leaked
- API keys and tokens
- Password hashes (comparing to stored hash)
- HMAC signatures
- Session tokens
- Any secret comparison
The Fix: Constant-Time Comparison
import crypto from 'crypto';
function safeCompare(a, b) {
// Must be same length for timingSafeEqual
if (a.length !== b.length) {
return false;
}
return crypto.timingSafeEqual(
Buffer.from(a),
Buffer.from(b)
);
}
// This always takes the same time regardless of where they differ
Length-Safe Version
function constantTimeCompare(provided, actual) {
// Use HMAC to normalize length
const hash = (s) => crypto
.createHmac('sha256', 'constant-key')
.update(s)
.digest();
return crypto.timingSafeEqual(
hash(provided),
hash(actual)
);
}
Is this attack practical over the network?
Over a local network, yes. Further out, still yes with enough samples: statistical analysis picks up timing differences down to microseconds. CDNs and cloud routing add noise, but noise isn't the same as safety.
Does bcrypt prevent this for passwords?
Bcrypt handles password storage. It doesn't automatically make the hash comparison constant-time, that's what bcrypt.compare() does under the hood, so use it instead of comparing hashes yourself.
Find Timing Vulnerabilities
Our scanner identifies timing-unsafe comparisons in your code.