DEV Community

Fahad Farid
Fahad Farid

Posted on

Your password generator is probably using Math.random(). Stop.

Math.random() is not cryptographically secure. Its output is deterministic given the internal state, which means passwords generated with it are guessable in principle. Any password or token generator must use the Web Crypto API instead: crypto.getRandomValues().

The correct core

function securePassword(length = 20) {
  const charset =
    'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%^&*';
  const bytes = new Uint8Array(length * 2);
  crypto.getRandomValues(bytes); // CSPRNG, provided by the OS
  const limit = 256 - (256 % charset.length);
  const out = [];
  for (const b of bytes) {
    // Rejection sampling: skip bytes that would cause modulo bias
    if (b < limit && out.length < length) out.push(charset[b % charset.length]);
  }
  return out.join('');
}
Enter fullscreen mode Exit fullscreen mode

Two details worth understanding:

  • crypto.getRandomValues() pulls from the operating system's cryptographically secure random number generator — the same source TLS uses. It cannot be seeded or predicted from JavaScript.
  • Rejection sampling fixes modulo bias. If your charset length doesn't divide 256 evenly, a naive byte % charset.length makes some characters slightly more likely. Discarding out-of-range bytes keeps the distribution uniform. (In production, loop and refill until the password is full rather than hoping one buffer is enough.)

How strong is it?

With a 70-character set and 20 characters, you get log2(70^20) ≈ 122 bits of entropy. That's beyond brute force for any realistic adversary — length plus a real CSPRNG is the entire game.

The checklist

  • Never Math.random() for secrets. Ever.
  • Generate locally — a password that never touches a network can't be intercepted in transit or logged by a server.
  • Don't get clever with "memorable" patterns that shrink the search space.

Want one without writing it yourself? The Password Generator on ToolVero uses exactly this approach — everything runs locally in your tab.

Top comments (0)