Random Numbers (CSPRNG)
Random number generation: when Math.random is fine and when it is dangerous. CSPRNGs in every major language, and how to test that your randomness is genuine.
Crypto — random number generation
EXAMPLE
# ===== Two kinds of random =====
# PRNG (Math.random, mt19937)
# Deterministic given the seed. Fast. Predictable.
# Use for: games, shuffles, simulations, fake data.
#
# CSPRNG (crypto.randomBytes, secrets.token_bytes, /dev/urandom)
# Unpredictable to attackers without the seed.
# Use for: tokens, session IDs, keys, salts, nonces, anything security-related.
# Mixing them up is the most common crypto bug in app code.
# ===== When you MUST use CSPRNG =====
# - Session tokens / cookies
# - Password reset / email verification tokens
# - API keys
# - Cryptographic keys + nonces
# - Salt for password hashing
# - CSRF tokens
# - Two-factor codes (TOTP secrets)
# - 'Random' URLs (signed S3 URLs, share links)
# ===== Per-language quick reference =====
# Node.js:
import { randomBytes, randomUUID, randomInt } from 'node:crypto';
randomBytes(32).toString('hex'); // 64-char hex token
randomUUID(); // v4 UUID, CSPRNG-backed
randomInt(0, 100); // CSPRNG int
# Python:
import secrets
secrets.token_hex(32) # CSPRNG hex
secrets.token_urlsafe(32) # URL-safe base64
secrets.choice([1, 2, 3])
# Go:
import ("crypto/rand"; "encoding/hex")
b := make([]byte, 32); rand.Read(b); hex.EncodeToString(b)
# Rust:
use rand::rngs::OsRng;
use rand::RngCore;
let mut b = [0u8; 32]; OsRng.fill_bytes(&mut b);
# Java:
SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] b = new byte[32]; sr.nextBytes(b);
# C#:
using System.Security.Cryptography;
var b = RandomNumberGenerator.GetBytes(32);
# Ruby:
require 'securerandom'
SecureRandom.hex(32)
# PHP:
random_bytes(32); // CSPRNG, throws on failure
random_int(0, 100);
# ===== UUID notes =====
# v4 UUID is random; some libraries use CSPRNG, some PRNG. Verify.
# Node randomUUID, Python uuid.uuid4 (CSPRNG), Java UUID.randomUUID (CSPRNG since 6+)
# ===== Testing your randomness =====
# Quick distribution sanity check:
python -c "import secrets; print({i: 0 for i in range(10)} | {})"
# Use dieharder / TestU01 for serious testing if rolling your own RNG.
# ===== Patterns to internalise =====
# - Default to the CSPRNG; never Math.random for security
# - 32-byte tokens (256 bits) are the safe baseline for opaque IDs
# - randomBytes -> hex / base64url for display
# - Salts: at least 16 bytes, unique per record
# ===== Pitfalls =====
# - Math.random() for session IDs -> predictable; account takeover risk
# - Re-seeding a CSPRNG with predictable data -> reduces entropy
# - Using time + pid as a 'random' seed -> predictable
# - Generating tokens in a loop without re-seeding entropy on long-running daemons
Why it matters
Use the CSPRNG. Math.random is for games; tokens deserve crypto.randomBytes. Every modern language has a one-liner — randomBytes, secrets.token_hex, SecureRandom, RandomNumberGenerator. Pick 32 bytes as the safe default and never look back.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Use a CSPRNG. Never Math.random / rand for security. // Node: crypto.randomBytes(n) / crypto.randomUUID() // Browser: crypto.getRandomValues(new Uint8Array(n)) // Python: secrets.token_bytes(n) // PHP: random_bytes(n) / random_int(0, 100)Try it Yourself »
Exercise
Node API for secure random bytes.
crypto.
(32)
camelCase.
Discussion
Loading…