Closes #22431: Replace random.choice with secrets.choice in Token.generate()#22432
Conversation
Token.generate() used Python's random module (Mersenne Twister PRNG). Mersenne Twister is not a CSPRNG: observing ~624 outputs from the same worker process allows full state recovery and prediction of subsequent outputs. Any token minted in the same worker within that window becomes predictable, including tokens for privileged accounts. Fix: replace random.choice with secrets.choice. secrets is backed by os.urandom() / getrandom() which provides OS-level CSPRNG entropy and is immune to state-recovery attacks. The import of the now-unused random module is removed. Regression tests: - test_generate_uses_csprng: patches secrets.choice with wraps= to confirm it is called exactly TOKEN_DEFAULT_LENGTH times per generate(). - test_generate_length_parameter: verifies length= is respected and output is drawn only from TOKEN_CHARSET. Ref: SR-001 / VM-317 (internal security review, R1-F07 / R3-F1) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7710e40 to
3e6e3ce
Compare
|
@claude /review and limit response only to areas of potential concern. |
|
Claude finished @bctiemann's task in 1m 33s —— View job Code Review: PR #22432
The fix itself is correct and minimal. Two areas worth noting: 1.
|
Closes: #22431
Summary
Replaces the non-CSPRNG
random.choice()call inToken.generate()withsecrets.choice()(SR-001 / VM-317, CWE-338).Root cause
Token.generate()usedrandom.choice(TOKEN_CHARSET)to build token strings. Python'srandommodule uses the Mersenne Twister PRNG, which is not cryptographically secure. Observing approximately 624 32-bit outputs from the same worker process is sufficient to recover the full PRNG state and predict all subsequent outputs — including tokens minted for other users (potentially administrators) by the same worker.Fix
Replace
import randomwithimport secretsand changerandom.choicetosecrets.choice.secretsis backed byos.urandom()/getrandom()(OS-level CSPRNG) and is immune to state-recovery attacks. This is a one-line logic change with no behavioural impact beyond improved entropy.The fix plan also requested an audit of other
random.*usages in auth-adjacent code; none were found.Tests
test_generate_uses_csprng— patchessecrets.choicewithwraps=to verify it is called exactlyTOKEN_DEFAULT_LENGTHtimes and that the output only containsTOKEN_CHARSETcharacters.test_generate_length_parameter— verifies thelength=parameter is respected across several values.Ref: SR-001 / VM-317 (internal security review, not an externally-reported disclosure)
🤖 Generated with Claude Code