Generating a random string and generating a secure random string are two different operations that can produce output that looks identical — but only one of them is safe to use as a session token, password-reset link, or API key
The previous articles on this site covered random-string length (the birthday problem), secure randomness (Math.random vs CSPRNG), and identifier formats (UUID, ULID, NanoID). This article addresses a practical question that sits between those topics: given that you need a random string for test data — not a security token — what's the right approach, and how should it differ from how you'd generate a security-critical string?
The two use cases, and why they require different sources
Use case 1: test data / placeholder values
You're generating usernames for a load test, fake email addresses to populate a dev database, random product SKUs for a staging environment, or placeholder strings to fill a form field in an automated test. Here, "random" means "unlikely to collide with something else I generate, and not a real person's data." The statistical randomness of the values matters; the cryptographic unpredictability does not — there's no adversary trying to predict your test usernames.
Use case 2: security-critical identifiers
You're generating a session token, a password-reset link, an API key, a CSRF token, or a magic-link for email authentication. Here, "random" must mean "cryptographically unpredictable" — an adversary who has seen 1,000 of your previously generated tokens must have no meaningful ability to predict the 1,001st. Using a source of randomness that's statistically reasonable but cryptographically weak (like most Math.random() implementations) makes these tokens guessable — which is a direct security vulnerability.
What makes randomness "cryptographically secure"
A Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) satisfies two properties:
- Statistical randomness — the output passes statistical tests for uniformity and independence (same requirement as any "good" PRNG)
- Next-state unpredictability — knowing any number of previous outputs provides no computational advantage in predicting future outputs, even to an adversary with substantial computing resources
Standard PRNGs (like Mersenne Twister, used in many Math.random() implementations) satisfy property 1 but explicitly fail property 2. The Mersenne Twister's internal state can be fully reconstructed from as few as 624 consecutive 32-bit outputs — after which, all future outputs are deterministically predictable. For test data, this doesn't matter. For session tokens, it's catastrophic.
Alphabet selection: a dimension beyond length
The length and source of a random string determine its security — but the alphabet (the set of characters it's drawn from) also matters, because it determines how much entropy each character contributes.
Common alphabet sizes:
- Hexadecimal (0-9, a-f): 16 characters — each character contributes 4 bits of entropy (log₂(16) = 4)
- Base64 (A-Z, a-z, 0-9, +, /): 64 characters — each character contributes 6 bits (log₂(64) = 6)
- Alphanumeric (A-Z, a-z, 0-9): 62 characters — each character contributes ~5.95 bits
- Full printable ASCII (33-126): 94 characters — each character contributes ~6.55 bits
Practical implication: a 32-character hex string provides 32 × 4 = 128 bits of entropy. A 32-character Base64 string provides 32 × 6 = 192 bits. Same length, meaningfully different entropy. For security tokens, this matters — for test data, it almost never does.
128 bits is commonly cited as a minimum for security tokens (sufficient that exhaustive guessing is computationally infeasible with current and foreseeable technology). This requires:
- ~32 hex characters
- ~22 Base64 characters
- ~39 binary (0/1) characters
Why "looks random" isn't "is random"
A string generated by a weak PRNG and a string generated by a CSPRNG are visually indistinguishable. 7f3a9c2b and d4e81fa0 look equally random to a human eye — but one might have been produced by a seed-predictable generator, and one by /dev/urandom. The process is what matters, not the output's appearance. This is the core reason "just use Math.random()" is dangerous for tokens even when the output "looks fine."
For test data: the additional concern of realistic vs random values
For test data specifically, there's a dimension that doesn't apply to security tokens: whether the test data resembles production data enough to expose real bugs.
Purely random strings as test email addresses ([email protected]) will reveal bugs in email storage and retrieval, but not in email parsing — because real email addresses have structure (local part, @, domain) that a random string doesn't respect. A test suite that only ever tests with syntactically valid email addresses will miss bugs triggered by edge cases in real email addresses (addresses with + in the local part, addresses with long domains, etc.).
The appropriate tool for test data is often structured fake data (libraries that generate plausible-looking but fake names, addresses, and emails — sometimes called "faker" libraries) rather than purely random strings — with purely random strings reserved for cases where the structure of the value doesn't matter (e.g., a free-text field with no format validation).
How to use the Random String Generator on sadiqbd.com
- For test data / placeholders: use any reasonable length and alphabet — the default settings are fine; cryptographic security is not a consideration here
- For security tokens: ensure you're using the tool's secure generation mode (if offered) — and if generating tokens programmatically in your own code, use platform-native CSPRNGs (
crypto.randomBytesin Node.js,secrets.token_hexin Python,SecureRandomin Java) rather than general-purpose random functions - For test data that needs to resemble production data: consider whether a random string is actually the right tool, or whether structured fake-data generation (faker libraries) would produce more realistic test cases
Frequently Asked Questions
If I use a CSPRNG for test data — am I "wasting" security on something that doesn't need it? Not meaningfully. CSPRNGs are not significantly slower than standard PRNGs for most practical purposes — the performance difference is negligible for generating strings one at a time (rather than millions per second in a tight loop). The cost of "always using a CSPRNG" is nearly zero. The benefit: you don't need to maintain a mental model of "this code path uses secure randomness, that code path doesn't." If your random-string utility always uses a CSPRNG, you never face the risk of accidentally reaching for the non-secure version in a context where security matters. The simplest policy — CSPRNG everywhere — is often more robust in practice than a two-tier approach.
Is the Random String Generator free? Yes — completely free, no sign-up required.
Try the Random String Generator free at sadiqbd.com — generate random strings for test data, tokens, and identifiers.