Why Random Strings Matter More Than You Think
Every modern application runs on randomness. Your session cookie, your API key, your database primary key, your password reset link, your OAuth state parameter, your CSRF token — every one of these is some form of random string. When the randomness is good, none of them are guessable. When the randomness is bad, your entire security model collapses, often invisibly.
The painful part: most developers learn this the hard way. They write Math.random().toString(36).substring(2, 15) because Stack Overflow''s top answer says to, ship it to production, and only discover months later that an attacker has predicted every "random" token they''ve issued because Math.random() is not, and was never, secure.
This guide covers what you actually need to know about generating random strings for security purposes — the difference between secure and insecure RNGs, how long your strings need to be, and what to use for which job.
If you just want to generate one right now, use the Random String Generator — it uses the browser''s cryptographic RNG and shows you the entropy in bits.
The Two RNGs Every Developer Must Distinguish
Browsers ship with two random number generators, and the difference between them is the entire security argument.
`Math.random()` is fast, deterministic given an internal state, and explicitly not for security. The V8 engine implements it as xorshift128+, a pseudo-random generator that produces statistically uniform output but whose internal state can be recovered from a small number of consecutive outputs. Mozilla''s documentation says it plainly: "Math.random() does not provide cryptographically secure random numbers. Do not use them for anything related to security."
`crypto.getRandomValues()` (and the higher-level crypto.randomUUID()) is the Web Crypto API''s CSRNG. It pulls from the operating system''s entropy source — /dev/urandom on Linux, BCryptGenRandom on Windows, SecRandomCopyBytes on macOS — the same source TLS uses for session keys. Outputs are unpredictable even to the system itself.
The trade-off is essentially zero. crypto.getRandomValues() is fast enough for any use case short of generating gigabytes of randomness per second, and the API is one line longer than Math.random(). There is no reason to use Math.random() for anything that matters.
// ❌ DON''T — predictable, attacker can recover state
const id = Math.random().toString(36).substring(2, 15);
// ✅ DO — cryptographically secure
const buf = new Uint8Array(24);
crypto.getRandomValues(buf);
const id = btoa(String.fromCharCode(...buf))
.replace(/\+/g, ''-'').replace(/\//g, ''_'').replace(/=/g, '''');The Random String Family Tree
Not every random string is the same kind of thing. The right length, character set, and structure depend on what the string represents.
Passwords are random strings that humans (or password managers) need to type, paste, and occasionally read aloud. Length beats complexity every time — XKCD''s "correct horse battery staple" pointed this out a decade ago and nothing has changed since. A 16-character mixed-case-plus-symbol password has roughly 105 bits of entropy; that''s strong enough that a brute-force attack at one trillion guesses per second would take longer than the age of the universe. If a human will type it, exclude ambiguous characters like 0/O and 1/l/I.
API keys identify a calling application across requests. The industry baseline is 128 bits of entropy — about 22 alphanumeric characters or 32 hex characters. Stripe''s sk_live_* keys are 32 characters of base62 after the prefix. GitHub PATs are 40 hex characters. AWS access keys are 20 characters of base32. All give well over 100 bits of entropy.
Session tokens & bearer tokens are short-lived secrets that prove who a client is on subsequent requests. OWASP calls for at least 64 bits of entropy, recommends 128 bits. JWTs are signed but unencrypted by default — anyone holding one can read its payload, so don''t put secrets there. For pure session IDs (the random string in a cookie), 22+ alphanumeric characters does the job.
CSRF tokens & nonces have the same threat model as session tokens — needs to be unguessable, doesn''t need to be human-readable. 22 alphanumeric characters is plenty. The key property is that a fresh token is generated per session (or per request, in stricter setups).
UUIDs vs random strings: UUIDs (especially v4) are random strings with a specific 128-bit format and a version marker. Use them when something else expects a UUID format — database UUID columns, GraphQL ID fields, distributed systems where the format is contractually required. Use a free-form random string when format doesn''t matter and you want fewer characters or a custom alphabet. DevPik has a dedicated UUID Generator for the formatted case.
Entropy: The One Number That Matters
If you remember nothing else from this article, remember entropy. Entropy is measured in bits and tells you, exactly, how unpredictable a random string is.
The formula is length × log2(charset_size). So:
- 8-character lowercase: 8 × log2(26) ≈ 38 bits. Crackable on a single GPU in hours.
- 12-character alphanumeric: 12 × log2(62) ≈ 71 bits. Marginal — would take a well-funded attacker months.
- 16-character mixed (all sets): 16 × log2(94) ≈ 105 bits. Safe against any current attacker.
- 22-character alphanumeric: 22 × log2(62) ≈ 131 bits. Industry-standard "secure".
- 32-character hex: 32 × log2(16) = 128 bits. The classic cryptographic threshold.
The standard threshold for "computationally infeasible to brute-force" is 80 bits. The standard threshold for "secure against a state-level attacker for the foreseeable future" is 128 bits. The DevPik Random String Generator shows entropy bits on every generation so you can pick a length deliberately rather than guessing.
API Key Best Practices
Generating the key is the easy part. Operating it well is what separates a robust system from a security incident.
Length: At least 22 alphanumeric characters (130+ bits). More is fine; more makes it harder to brute-force the keyspace if you ever leak a hash.
Character set: URL-safe base64 (A-Z a-z 0-9 - _) is the default. Avoid + and / (they need URL-encoding) and = padding. Hex is fine but takes more characters for the same entropy.
Prefix: Add a service-identifying prefix (Stripe''s sk_live_, GitHub''s ghp_). Why? Two reasons. First, GitHub''s secret scanner can recognize your keys in public repos and notify you. Second, when an engineer pastes a key into the wrong terminal, the prefix lets you immediately see what it is.
Storage: Hash the key in your database with bcrypt or Argon2. Show the full key to the user exactly once — at creation time. Never give users a way to retrieve an old key; they have to rotate. This way, a database leak doesn''t expose live keys.
Rotation: Build the rotation flow on day one. Issue every account two simultaneously valid keys; deprecate the old one a week later. If you don''t have a working rotation flow, you cannot respond to a leaked-key incident without taking the customer offline.
Logging: Never log full keys. Log a stable prefix (first 8 chars) plus the last 4. That gives ops enough to identify which key is making which request without putting the secret in your log retention.
Common Mistakes — A Hall of Shame
- Using `Math.random()` for any security-relevant value. Already covered. The single most common bug.
- Insufficient length. A 6-character random string is only 35 bits of entropy. That''s a billion possibilities — crackable on a GPU in seconds.
- Predictable character sets. Using only digits (
Math.random() * 1000000for an OTP) cuts entropy roughly 3x compared to alphanumeric. For OTPs that''s acceptable because they''re short-lived and rate-limited; for API keys it''s not.
- Encoding timestamps. ULIDs and KSUIDs do this on purpose for sortability, but a homemade scheme that prefixes a timestamp without enough random tail is guessable. If you generate two tokens for the same user in the same second, an attacker who sees one can dramatically narrow the search space for the other.
- Reusing session tokens after login. If you don''t rotate the session ID on privilege change (login, role change), session-fixation attacks become trivial.
- Putting secrets in JWT payloads. JWTs are signed but unencrypted. Anyone with the token can decode the payload. Use JWT Decoder to confirm what you''re actually sending; if you need to put sensitive data in the token, use an encrypted variant (JWE).
- Generating keys on the client and trusting them. A "random" string generated in a JavaScript bundle that the client controls is not random from the server''s perspective — the client could choose anything. Generate on the server, return to the client, and treat the returned value as the source of truth.
Quick Reference: What to Use When
| Use case | Length | Charset | Entropy |
|---|---|---|---|
| User-typed password | 16 chars | All sets | ~105 bits |
| Generated password (in PW manager) | 24 chars | All sets | ~157 bits |
| API key | 32 chars | Alphanumeric | ~190 bits |
| Session token | 22 chars | URL-safe base64 | ~131 bits |
| CSRF token | 22 chars | URL-safe base64 | ~131 bits |
| Email verification token | 32 chars | URL-safe base64 | ~191 bits |
| File upload ID | 16 chars | Alphanumeric | ~95 bits |
| One-time code (OTP) | 6 digits | Digits | ~20 bits + rate limiting |
| Database primary key | UUID v4 | Hex + dashes | 122 bits |
Generate Securely in One Click
The DevPik Random String Generator uses crypto.getRandomValues() for every output, shows entropy bits live, and includes presets for passwords, API keys, hex strings, tokens, and variable names. Bulk mode generates up to 100 strings at once for seeding test data or pre-issuing tokens. Need a UUID instead? The UUID Generator handles v4 generation with the same RNG.
Generate secure random strings, passwords, and API keys instantly with our free Random String Generator. Try our 49+ free developer tools.





