The Login Endpoint That Leaks Timing
A team is reviewing its authentication service. Login accepts an email and password, looks up the user in a database, and compares the password hash with bcrypt. Error messages are generic ("Invalid credentials"). However, the security team observed that requests to the login endpoint take about 40 ms for nonexistent emails and about 220 ms for existing emails. The team plans to fix this by adding a random delay of 0โ300 ms to every login response. They also currently store failed-login counters in memory on each of four app servers behind a load balancer, and lock accounts after five failures per server. The product manager wants to keep the login flow unchanged for users and asks for a concrete remediation plan that does not require a full rewrite.
1. Evaluate the proposed random-delay fix. What does it actually accomplish, and what should be done instead?
Read the worked answer
A random 0โ300 ms delay does not remove the timing difference; it only adds noise. With repeated requests, an attacker can average many samples and still distinguish the two populations (40 ms vs. 220 ms), especially because the gap is larger than the delay's variance. The correct fix is to make the work performed constant-time with respect to whether the account exists: always perform a bcrypt verification against a fixed dummy hash when the email is not found, so both branches do comparable work. The random delay can remain as defense in depth but must not be the primary control. Also consider rate limiting and generic responses at the edge. The key reasoning is that timing side channels are statistical, so any fix must equalize the underlying work, not obscure it.
2. The account lockout counter is per-server in memory. Explain the security and availability consequences and propose a better design.
Read the worked answer
With four servers, an attacker gets up to 5 attempts per server before lockout, and because the load balancer distributes requests, the effective threshold is up to 20 attempts, and the attacker can rotate source IPs to spread attempts further. Worse, a legitimate user can be locked out on one server while still able to log in on another, producing inconsistent behavior. A better design stores counters in a shared, atomic store such as Redis, keyed by account and optionally by source IP, with a sliding window. Use exponential backoff rather than a hard lock, and add CAPTCHA or step-up verification after repeated failures. Also ensure the counter update is atomic to avoid race conditions under concurrent attempts. This addresses both brute-force resistance and availability for legitimate users.