gotchajavascriptMajor
Password Hashing Timing Attacks and Safe Comparison
Viewed 0 times
timing attacktiming safe equalconstant time comparisoncryptopassword comparisonside channel
Problem
Using a regular string equality check (===) to compare password hashes leaks timing information. An attacker can measure response times to learn how many characters of a hash match.
Solution
Use crypto.timingSafeEqual() for any security-sensitive comparison. For passwords specifically, always use the hashing library's built-in verify function which handles this.
Why
Standard equality checks short-circuit at the first mismatched character. crypto.timingSafeEqual always takes the same time regardless of where the inputs differ, eliminating the timing signal.
Gotchas
- crypto.timingSafeEqual requires both buffers to be the same length—pre-check or pad, otherwise it throws
- Even with safe comparison, hash the provided value first before comparing—otherwise you are comparing a plaintext to a hash
- HMAC-based comparison is also safe because HMAC operations take constant time for the same input length
- Timing attacks require many samples to be practical—they are still a real threat for APIs that can be queried rapidly
Code Snippets
Constant-time token comparison using crypto.timingSafeEqual
const crypto = require('crypto');
function safeCompare(a, b) {
// Ensure same length to prevent length-based timing leaks
const bufA = Buffer.from(a, 'utf8');
const bufB = Buffer.from(b, 'utf8');
if (bufA.length !== bufB.length) {
// Compare anyway to prevent short-circuit—but still return false
crypto.timingSafeEqual(bufA, Buffer.alloc(bufA.length));
return false;
}
return crypto.timingSafeEqual(bufA, bufB);
}
// For passwords, prefer argon2.verify() which handles this internally
// For API tokens and HMAC signatures, use safeCompareRevisions (0)
No revisions yet.