OAuth2 刷新令牌重用检测失败导致令牌被盗未被发现
OAuth2 refresh token reuse detection failure allows token theft to go unnoticed
ID: security/oauth2-refresh-token-reuse-detection-failure
版本兼容性
| 版本 | 状态 | 引入 | 弃用 | 备注 |
|---|---|---|---|---|
| OAuth2 2.0 | active | — | — | — |
| Spring Security 6.2.0 | active | — | — | — |
| Keycloak 23.0.0 | active | — | — | — |
| Auth0 Node.js SDK 2.45.0 | active | — | — | — |
根因分析
授权服务器未实现刷新令牌轮换或重用检测,因此如果攻击者窃取刷新令牌并使用它,合法用户的令牌仍然有效,允许攻击者无限期地继续获取新的访问令牌。
English
The authorization server does not implement refresh token rotation or reuse detection, so if an attacker steals a refresh token and uses it, the legitimate user's token remains valid, allowing the attacker to continue obtaining new access tokens indefinitely.
官方文档
https://datatracker.ietf.org/doc/html/rfc6749#section-10.4解决方案
-
实现刷新令牌轮换:每次使用刷新令牌时发放新的刷新令牌,并使旧的失效。在 Spring Security 中,在授权服务器配置中将 `refreshTokenRotationStrategy` 设置为 `ROTATE`。示例:`@Bean public OAuth2AuthorizationServerConfigurer authorizationServerConfigurer() { return new OAuth2AuthorizationServerConfigurer() .refreshTokenRotationStrategy(RefreshTokenRotationStrategy.ROTATE); }`。 -
实现刷新令牌重用检测:如果刷新令牌被多次使用,则撤销该客户端和用户的所有令牌。在 Keycloak 中,在客户端设置的“高级设置”下启用“撤销刷新令牌”。这将在重用时使令牌系列无效。
-
结合使用轮换和重用检测。将上次使用的刷新令牌存储在数据库中,并在每次请求时进行比较。如果令牌之前已被使用,则撤销所有令牌并提醒用户。示例伪代码:`if (token in usedTokens) { revokeAllTokens(user); alert("检测到安全漏洞"); } else { usedTokens.add(token); issueNewToken(); }`。
无效尝试
常见但无效的做法:
-
Set a very short expiration time for refresh tokens (e.g., 5 minutes)
50% 失败
Short expiration reduces the window of opportunity but does not prevent reuse. An attacker can still use the token within that window, and the legitimate user's session is interrupted.
-
Use IP-based validation to detect unusual refresh token usage
60% 失败
IP addresses can be spoofed or changed (e.g., via VPN), and legitimate users may have dynamic IPs, leading to false positives. This is not a reliable security measure.
-
Block the user account after a single refresh token use
80% 失败
This would break legitimate usage where users refresh tokens normally. It also does not distinguish between the legitimate user and the attacker.