Login response time differs for valid vs invalid usernames enabling user enumeration
ID: security/timing-attack-user-enumeration-login
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| Node.js 20 | active | — | — | — |
| Python 3.11 | active | — | — | — |
| Java 17 | active | — | — | — |
Root Cause
The authentication logic performs a password hash comparison only after a database lookup confirms the user exists, causing a measurable time difference between requests for existing and non-existing usernames.
generic中文
认证逻辑仅在数据库查询确认用户存在后才进行密码哈希比较,导致现有用户名和不存在用户名的请求之间出现可测量的时间差异。
Official Documentation
https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.htmlWorkarounds
-
90% success Always perform a dummy password hash comparison, even when the user does not exist. In Node.js with bcrypt: const hash = user ? user.hash : DUMMY_HASH; await bcrypt.compare(password, hash);
Always perform a dummy password hash comparison, even when the user does not exist. In Node.js with bcrypt: const hash = user ? user.hash : DUMMY_HASH; await bcrypt.compare(password, hash);
-
85% success Use a constant-time comparison function like crypto.timingSafeEqual for all password checks, and ensure the same code path executes regardless of user existence.
Use a constant-time comparison function like crypto.timingSafeEqual for all password checks, and ensure the same code path executes regardless of user existence.
-
80% success Implement a generic error message for both invalid username and invalid password, and log the specific reason server-side only.
Implement a generic error message for both invalid username and invalid password, and log the specific reason server-side only.
中文步骤
即使当用户不存在时,也始终执行虚拟密码哈希比较。在Node.js中使用bcrypt:const hash = user ? user.hash : DUMMY_HASH; await bcrypt.compare(password, hash);
对所有密码检查使用常数时间比较函数,如crypto.timingSafeEqual,并确保无论用户是否存在,都执行相同的代码路径。
对无效用户名和无效密码都实现通用错误消息,并仅在服务器端记录具体原因。
Dead Ends
Common approaches that don't work:
-
50% fail
Adding artificial delays to all responses increases latency for legitimate users and may not fully mask the timing difference if the delay is predictable.
-
60% fail
Removing the user existence check entirely breaks the authentication flow and may allow login with any password for non-existent users unless handled carefully.
-
40% fail
Using a simple sleep() function after a failed login is easily bypassed by attackers who can sample multiple requests and average out the delay.