AUTH_RACE_002
security
runtime_error
ai_generated
partial
Race condition in token revocation allows stale token reuse
ID: security/race-condition-in-token-revocation
78%Fix Rate
82%Confidence
1Evidence
2024-01-10First Seen
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| JWT 0.12.0 | active | — | — | — |
| OAuth2 2.0 | active | — | — | — |
| Redis 7.2 | active | — | — | — |
Root Cause
A race condition occurs when a token is revoked but a concurrent request validates it before the revocation is fully committed to the database, allowing stale token reuse.
generic中文
当令牌被撤销但并发请求在撤销完全提交到数据库之前验证它时,会发生竞态条件,允许重复使用过期令牌。
Official Documentation
https://oauth.net/2/token-revocation/Workarounds
-
85% success Use a distributed lock (e.g., Redis Redlock) around the revocation and validation operations to ensure atomicity. Example in Node.js: const lock = await redisClient.acquireLock('token_revoke_lock', 5000); try { await revokeTokenInDB(tokenId); // Validation will now see the revoked state } finally { await lock.release(); }
Use a distributed lock (e.g., Redis Redlock) around the revocation and validation operations to ensure atomicity. Example in Node.js: const lock = await redisClient.acquireLock('token_revoke_lock', 5000); try { await revokeTokenInDB(tokenId); // Validation will now see the revoked state } finally { await lock.release(); } -
80% success Implement a short blacklist (5-10 seconds) in memory (e.g., Guava Cache) for recently revoked tokens before checking the database, reducing the race window.
Implement a short blacklist (5-10 seconds) in memory (e.g., Guava Cache) for recently revoked tokens before checking the database, reducing the race window.
中文步骤
Use a distributed lock (e.g., Redis Redlock) around the revocation and validation operations to ensure atomicity. Example in Node.js: const lock = await redisClient.acquireLock('token_revoke_lock', 5000); try { await revokeTokenInDB(tokenId); // Validation will now see the revoked state } finally { await lock.release(); }Implement a short blacklist (5-10 seconds) in memory (e.g., Guava Cache) for recently revoked tokens before checking the database, reducing the race window.
Dead Ends
Common approaches that don't work:
-
Use a simple cache like Memcached with a short TTL to store revoked tokens
85% fail
Caching with TTL can expire before the revocation is processed, still allowing reuse; also, cache inconsistency across nodes worsens the race.
-
Add a small sleep() before validation after revocation
90% fail
Sleeping is non-deterministic and doesn't guarantee atomicity; it also degrades performance.