Back to Blog
SecurityAuthenticationNode.jsFeatured

Timing-Safe Password Verification: Stopping Login Attacks

·8 min

Implementing escalating lockout tiers (15min → 1hr → 24hr), dummy hash techniques, and timing-attack resistant authentication without slowing down legitimate users.

The Timing Side-Channel

Password verification appears simple: hash the input, compare to the stored hash, accept or reject. But this simple operation can leak information through a channel that seems innocuous—timing.

Standard string comparison short-circuits. It returns false as soon as it finds a mismatched character. If the first character differs, comparison takes one operation. If they match until the last character, it takes n operations.

An attacker can measure these timing differences. By trying different passwords and observing response times, they can infer how many characters matched. Character by character, they reconstruct the hash—without ever needing to break the cryptography.

Constant-Time Comparison

The defense is constant-time comparison: examining every byte regardless of whether a mismatch has already been found. The comparison accumulates differences using XOR, only checking the result at the end.

This ensures that matching and non-matching inputs take identical time. The attacker learns nothing from timing because timing is independent of input.

The technique requires care. Compilers optimize, and an optimization that short-circuits your "constant-time" code reintroduces the vulnerability. Cryptographic libraries provide primitives specifically designed to resist such optimizations.

The Missing Account Problem

Constant-time comparison fixes hash comparison, but creates a new leak. What happens when the account doesn't exist?

If login attempts on non-existent accounts return immediately—skipping the expensive hash operation—timing reveals which accounts exist. Attackers enumerate valid usernames by watching for fast versus slow responses.

The solution: always perform hash verification. When no account is found, verify against a dummy hash anyway. The timing matches real accounts because the same work is done.

Timing at Scale

These timing differences are tiny—microseconds or nanoseconds. How can attackers measure them across network latency that varies by milliseconds?

Statistics. A single request tells you nothing. A thousand requests, averaged, reveal patterns. Network jitter is random noise; the timing signal is consistent. With enough samples, the signal emerges.

Modern research demonstrates practical timing attacks over the internet, across continents. The noise floor is high but not infinite.

Lockout Strategies

Beyond timing attacks, brute force is the straightforward threat. Try every possible password until one works.

The obvious defense—lock the account after N failures—creates a different vulnerability. Attackers can lock any account by deliberately failing authentication. Denial of service through the security mechanism.

Better approaches use escalating delays or rate limiting that doesn't permanently lock out legitimate users. Track failures across multiple dimensions: per account, per IP, per account-IP combination. Apply decay over time so transient failures don't accumulate into permanent lockout.

The Slow Hash

The most important timing property is one we add deliberately: slow password hashing. Algorithms like bcrypt, scrypt, and Argon2 are designed to be computationally expensive.

A fast hash lets attackers try billions of passwords per second. A hash that takes 250 milliseconds limits them to four attempts per second per machine. The economics of brute force change completely.

This slowness is the primary defense. Timing-safe comparison and dummy hashes prevent information leakage, but slow hashing makes even leaked information less useful.

Defense in Depth

No single technique suffices. Timing-safe comparison closes one side-channel. Dummy hashes close another. Rate limiting constrains throughput. Slow hashing makes each attempt expensive.

An attacker who defeats one layer still faces the others. This redundancy is the essence of security engineering: assume any individual control can fail, and design systems that remain secure when they do.

The Broader Lesson

Timing attacks illustrate a general principle: computation leaks information through every observable property. Time, power consumption, electromagnetic emissions, cache behavior—each is a potential channel.

Cryptography provides mathematical security. Implementation security is different and often harder. The algorithm may be unbreakable, but the implementation is always physical, always measurable, always potentially revealing.

Secure systems require thinking about what's observable, not just what's mathematically proven. The side-channel is wherever you forgot to look.

Questions about this article?

Happy to dive deeper on any of these topics.

Get in Touch