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

Also available as: JSON · Markdown · 中文
78%Fix Rate
82%Confidence
1Evidence
2024-01-10First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
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

  1. 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();
    }
  2. 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.

中文步骤

  1. 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();
    }
  2. 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:

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

  2. Add a small sleep() before validation after revocation 90% fail

    Sleeping is non-deterministic and doesn't guarantee atomicity; it also degrades performance.