Tools · Cryptography and passwords in practice · 16 / 20
Secure random numbers: why Math.random and random.random are not for secrets
Generate tokens, keys, and passwords with a cryptographically secure random number generator in Python, JavaScript, and the shell, and recognize insecure generators in code reviews.
Predictable versus unpredictable
General-purpose generators such as Python’s random module (Mersenne Twister) and JavaScript’s Math.random are designed for simulations. Their internal state can be reconstructed from enough outputs, making future values predictable. Session tokens, password reset links, API keys, and nonces need a cryptographically secure generator (CSPRNG) backed by the operating system.
The right APIs
Python: the secrets module (token_bytes, token_hex, token_urlsafe, choice) or os.urandom. JavaScript: crypto.getRandomValues in browsers and crypto.randomBytes or crypto.randomUUID in Node.js. Shell: /dev/urandom, openssl rand. Go: crypto/rand. Java: SecureRandom.
# Python
import secrets, string
print(secrets.token_urlsafe(32)) # reset token
alphabet = string.ascii_letters + string.digits
print(''.join(secrets.choice(alphabet) for _ in range(20)))
// JavaScript (browser)
const buf = new Uint8Array(32); crypto.getRandomValues(buf);
// Node.js
require('crypto').randomBytes(32).toString('hex'); crypto.randomUUID();
# shell
head -c 32 /dev/urandom | base64
openssl rand -base64 32Lab: code review hunt
Search a codebase you own or a small open-source project for uses of random.random, random.randint, Math.random, rand(), or mt_rand near words like token, secret, password, reset, or key. For each hit, decide whether it is security-relevant and rewrite it with the right API. A quick search helps.
rg -n --type-add 'code:*.{py,js,ts,php,go,java}' -t code \
-e 'Math\.random\(' -e 'random\.(random|randint|choice)\(' -e '\bmt_rand\(' -e '\brand\(' \
| rg -i 'token|secret|pass|reset|key|nonce|otp'How much randomness
For tokens, 128 bits (16 bytes) of randomness is a common minimum; 256 bits is comfortable. Encoding changes length but not entropy: 32 random bytes as hex is 64 characters and still 256 bits. UUID version 4 has 122 random bits, which is fine as an identifier from a CSPRNG but should not be your only secret if a library might generate it differently.
Diagram
General-purpose versus cryptographic generator
Same-looking output, very different guarantees.
Designed for. Simulations, games
Predictable?. Yes, from observed outputs
Use for secrets. Never
Designed for. Keys, tokens, nonces
Predictable?. No, OS-backed CSPRNG
Use for secrets. Always
Check yourself
1. Which Python API should generate a password reset token?
2. Why is Math.random unsuitable for session tokens?
3. How many random bytes give 256 bits of entropy?