有效和无效用户名的登录响应时间不同,导致用户枚举
Login response time differs for valid vs invalid usernames enabling user enumeration
ID: security/timing-attack-user-enumeration-login
版本兼容性
| 版本 | 状态 | 引入 | 弃用 | 备注 |
|---|---|---|---|---|
| Node.js 20 | active | — | — | — |
| Python 3.11 | active | — | — | — |
| Java 17 | active | — | — | — |
根因分析
认证逻辑仅在数据库查询确认用户存在后才进行密码哈希比较,导致现有用户名和不存在用户名的请求之间出现可测量的时间差异。
English
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.
官方文档
https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html解决方案
-
即使当用户不存在时,也始终执行虚拟密码哈希比较。在Node.js中使用bcrypt:const hash = user ? user.hash : DUMMY_HASH; await bcrypt.compare(password, hash);
-
对所有密码检查使用常数时间比较函数,如crypto.timingSafeEqual,并确保无论用户是否存在,都执行相同的代码路径。
-
对无效用户名和无效密码都实现通用错误消息,并仅在服务器端记录具体原因。
无效尝试
常见但无效的做法:
-
50% 失败
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% 失败
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% 失败
Using a simple sleep() function after a failed login is easily bypassed by attackers who can sample multiple requests and average out the delay.