Blog/Cryptography
CryptographyJuly 21, 2026 · 6 min read

Timing Attacks: When == Leaks Your Secret One Byte at a Time

You verify an API token the obvious way:

if (providedToken === storedToken) grantAccess();

That === is a side channel. String comparison stops at the first differing character, so a token guess that gets the first byte right takes a hair longer to reject than one that gets it wrong. An attacker who can measure response time submits a..., b..., c..., finds that one branch is consistently slower, learns the first byte, then repeats for the second — extracting the whole secret one byte at a time. A 32-character token that would take longer than the universe to brute force falls in a few thousand timed requests. This is a timing attack, and the fix is to compare secrets in a way that always takes the same amount of time.

Where the leak lives

Anywhere you compare a secret with ==, ===, .equals(), or memcmp:

  • API key / token verification on every authenticated request
  • HMAC / webhook signature checks (signature === expected)
  • Password reset and email-verification tokens
  • Coupon, license, and CSRF token validation
  • Password checks that compare raw values instead of verifying a hash (which has its own constant-time compare inside)

The tell is a short-circuiting comparison whose runtime depends on how much of the input matched.

The fix: constant-time comparison

Constant-time comparison inspects every byte regardless of where a mismatch occurs and folds the differences together, so the duration is independent of the inputs' contents. Never hand-roll it — the compiler may optimize your loop back into an early return. Use your platform's crypto primitive.

const crypto = require('crypto');

// timingSafeEqual requires equal-length buffers and compares in
// constant time. Hash both sides first so length itself doesn't leak
// and lengths always match.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(a).digest();
const hb = crypto.createHash('sha256').update(b).digest();
return crypto.timingSafeEqual(ha, hb);
}

// API token check:
if (safeEqual(providedToken, storedToken)) grantAccess();

// Webhook signature verification (constant-time by construction):
function verifyWebhook(body, signature, secret) {
const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');
return safeEqual(signature, expected);
}

The principles

  • Compare secrets in constant time. Use timingSafeEqual / compare_digest / hash_equals for tokens, keys, and signatures — anything where a match grants access.
  • Verify passwords with a slow hash, never a direct compare. bcrypt.checkpw / password_verify handle the constant-time step for you.
  • Hash before comparing when lengths might differ, so the length itself doesn't leak and the constant-time primitive's equal-length requirement is met.
  • Add rate limiting as defense in depth. Timing attacks need many samples; throttling and lockouts raise the cost.

The one line: if a comparison decides access, its running time must not depend on the secret — use the constant-time primitive your crypto library ships.

Try it in the timing attack simulation: recover a token byte by byte from response times, then switch to constant-time comparison and watch the signal vanish. It complements the HMAC signatures guide, where the same constant-time rule applies to signature checks.

Share this post

Frequently Asked Questions

Related posts

Padding Oracle Attacks: Decrypting Without the Key

A padding oracle lets an attacker decrypt CBC ciphertext one byte at a time — without ever knowing the key — just from whether the server says 'bad padding.' Here's how the attack works and why authenticated encryption ends it.

Jul 25, 2026 · 7 min read

EC Keys and ECDSA: The Faster Alternative to RSA for JWT Signing

Learn how elliptic curve keys work, why P-256 is the right default for JWT ES256, and how ECDSA compares to RSA in size, speed, and security.

Mar 13, 2026 · 6 min read

AES vs RSA — Which Encryption Should You Use in Your App?

A practical guide for backend developers on when to use AES symmetric encryption vs RSA asymmetric encryption. Includes real-world use cases, code examples in JavaScript, Python, Java, PHP, Kotlin, and Swift.

Mar 12, 2026 · 10 min read