HackBlue

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.

8 min read · practice only with authorization

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.

Snippet
# 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 32

Lab: 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.

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

random / Math.random

Designed for. Simulations, games

Predictable?. Yes, from observed outputs

Use for secrets. Never

secrets / crypto.getRandomValues

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?