Secure Random String Generation for Developers
Why Math.random() is not enough for API keys and tokens, how cryptographically secure random number generators work, and how to choose the right length and character set.
PRNG vs CSPRNG
A Pseudo-Random Number Generator (PRNG) like Math.random() produces numbers that appear random but are derived from a deterministic seed. Given enough output, an attacker can predict future values. This makes PRNGs completely unsuitable for security-sensitive strings.
A Cryptographically Secure PRNG (CSPRNG) sources entropy from the operating system (hardware events, interrupt timings) and is designed to be computationally infeasible to predict. In the browser this is crypto.getRandomValues(); in Node.js it is crypto.randomBytes().
Browser CSPRNG
// Fill a typed array with secure random bytes
const bytes = new Uint8Array(32);
crypto.getRandomValues(bytes);Building a Random String from Random Bytes
Once you have random bytes, map them to a character set:
function randomString(length, charset) {
const bytes = new Uint8Array(length);
crypto.getRandomValues(bytes);
return Array.from(bytes)
.map(b => charset[b % charset.length])
.join('');
}
const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const token = randomString(32, ALPHA);Note: using b % charset.length introduces a tiny modulo bias when the charset length does not evenly divide 256. For most use cases this bias is negligible. For high-security applications, use rejection sampling: discard any byte ≥ floor(256 / charset.length) * charset.length.
How Long Should a Random String Be?
Security comes from entropy — the number of possible values. An N-character string over an alphabet of size A has AN possible values. The entropy in bits is N × log₂(A).
- 32 chars, alphanumeric (62): ~190 bits — suitable for API keys and session tokens
- 64 chars, alphanumeric: ~381 bits — suitable for long-lived secrets
- 16 chars, hex (16): 64 bits — minimum for low-value nonces
- 128 bits is the commonly accepted minimum for secrets that need to withstand brute force
Character Set Trade-offs
Alphanumeric (A-Z, a-z, 0-9)
62 characters, URL-safe, copy-paste friendly, readable. Best for API keys and tokens that appear in config files or headers.
Hex (0-9, a-f)
16 characters — only 4 bits per character. Requires longer strings for the same entropy but is universally safe in any context (URLs, JSON, headers, filenames).
Base64url
64 characters (A-Z, a-z, 0-9, -, _). URL-safe variant of Base64 without padding. Common in JWT and OAuth tokens. 6 bits per character — good balance of compactness and safety.
Full printable ASCII
95 characters. Maximum density per character — good for passwords where humans need to type them. Avoid ambiguous characters (O, 0, l, 1) if readability matters.
Common Use Cases
- API keys: 32–48 alphanumeric chars with a vendor prefix (e.g.,
sk_live_...) - Session tokens: 32+ chars, stored server-side, invalidated on logout
- CSRF tokens: 32 chars, per-form, single-use
- Password reset links: 32+ chars, short TTL (15–60 min)
- Invite codes: 8–12 chars, can use smaller charset for readability
What to Avoid
- Math.random(): Predictable, never use for security
- Timestamps as tokens: Guessable; narrow search space
- UUIDs for secrets: UUID v4 has only 122 bits of randomness and v1 leaks the MAC address — better to use raw random bytes
- Short strings: Under 128 bits entropy is exploitable given enough attempts
Try it yourself
Generate cryptographically secure random strings — choose length, charset, and quantity.
Open Random String Generator →