security auth_error ai_generated true

OAuth2 刷新令牌重用检测失败导致令牌被盗未被发现

OAuth2 refresh token reuse detection failure allows token theft to go unnoticed

ID: security/oauth2-refresh-token-reuse-detection-failure

其他格式: JSON · Markdown 中文 · English
87%修复率
84%置信度
1证据数
2024-09-12首次发现

版本兼容性

版本状态引入弃用备注
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.

generic

官方文档

https://datatracker.ietf.org/doc/html/rfc6749#section-10.4

解决方案

  1. 实现刷新令牌轮换:每次使用刷新令牌时发放新的刷新令牌,并使旧的失效。在 Spring Security 中,在授权服务器配置中将 `refreshTokenRotationStrategy` 设置为 `ROTATE`。示例:`@Bean public OAuth2AuthorizationServerConfigurer authorizationServerConfigurer() { return new OAuth2AuthorizationServerConfigurer() .refreshTokenRotationStrategy(RefreshTokenRotationStrategy.ROTATE); }`。
  2. 实现刷新令牌重用检测:如果刷新令牌被多次使用,则撤销该客户端和用户的所有令牌。在 Keycloak 中,在客户端设置的“高级设置”下启用“撤销刷新令牌”。这将在重用时使令牌系列无效。
  3. 结合使用轮换和重用检测。将上次使用的刷新令牌存储在数据库中,并在每次请求时进行比较。如果令牌之前已被使用,则撤销所有令牌并提醒用户。示例伪代码:`if (token in usedTokens) { revokeAllTokens(user); alert("检测到安全漏洞"); } else { usedTokens.add(token); issueNewToken(); }`。

无效尝试

常见但无效的做法:

  1. 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.

  2. 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.

  3. 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.