Learn
Why Math.random() Can't Make Secure Passwords
5 min read
Search for “JavaScript password generator” and half the snippets you find will loop over a character set with Math.random(). The output looks perfectly random — a soup of letters, digits, and symbols. It isn’t. The generator behind it was never designed to keep secrets, and treating it as if it were is one of the quieter security mistakes on the web.
What Math.random() actually is
Math.random() is a pseudo-random number generator: a deterministic algorithm that stretches one small seed into an endless-looking stream of numbers. In V8 — the engine inside Chrome and Node.js — it is xorshift128+, a generator with just 128 bits of internal state that produces each value with a few shift and XOR operations. It was chosen for speed and decent statistical quality, which is exactly what a game loop or a particle animation needs.
The problem is that determinism cuts both ways. The engine seeds the generator once, and everything after that follows mechanically. If someone can observe a handful of consecutive outputs — say, from a page that leaks a few random values, or from tokens generated alongside your password — they can solve for the 128 bits of hidden state. Tools that do this for xorshift128+ are public, and the recovery takes seconds using an off-the-shelf constraint solver. Once the state is known, every past and future output of that generator is known too. Your “random” password becomes a computable fact.
Note that this is a property of the design, not a bug to be patched. Math.random() makes no security promises — the ECMAScript specification explicitly leaves the algorithm to the implementation and says nothing about unpredictability. Predictable-but-fast is the contract.
What a CSPRNG does differently
A cryptographically secure pseudo-random number generator (CSPRNG) is built against a stronger contract: even an attacker who records unlimited output must be unable to predict the next bit or reconstruct earlier ones. Two things make that possible:
- —Seeding from real entropy — the operating system continuously gathers unpredictability from hardware events (interrupt timing, device noise, CPU thermal jitter) and exposes it via /dev/urandom or the getrandom() system call
- —One-way output — the stream is produced by cryptographic primitives, so working backwards from outputs to state is as hard as breaking the cipher itself
In the browser, the CSPRNG lives behind a single call: crypto.getRandomValues(). It is available in every modern browser and in Node.js, and it costs you nothing meaningful in performance. Here is a secure random pick from a character set, including the detail most snippets miss — rejection sampling to avoid modulo bias when the character count doesn’t divide evenly into 256:
const CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%";
function securePassword(length) {
const max = 256 - (256 % CHARS.length); // reject to stay uniform
let out = "";
while (out.length < length) {
const byte = crypto.getRandomValues(new Uint8Array(1))[0];
if (byte < max) out += CHARS[byte % CHARS.length];
}
return out;
}The rejection step matters. If you simply take byte % CHARS.length for every byte, characters early in the set come up slightly more often than the rest — a small bias, but one that compounds across a whole password and hands an attacker a head start they didn’t earn.
The rule of thumb
Deciding which generator to use takes one question: does anyone gain from predicting this value?
- —Nobody gains — animations, screen-shake, procedural visuals, picking a random placeholder — Math.random() is fine
- —Someone gains — passwords, session tokens, API keys, recovery codes, raffle winners, anything worth cheating — crypto.getRandomValues(), always
This is the standard PickRandom builds on: every tool on this site, from the password generator down to a simple coin flip, draws its randomness from crypto.getRandomValues(). The secure generator is no slower for human-scale use, so there is no trade-off left to weigh — only a habit to change. When in doubt, reach for the CSPRNG; the only thing you lose is the ability to be predicted.
Try it yourself
- —Password Generator — Create secure, random passwords with customizable length and character types.