Security Engineering

⚠️ Why Math.random() is Never Acceptable for Password Generation

By Ateeq Y Tanoli, · 7 May 2026 · 3 min read · 497 words

Why Math.random() is Never Acceptable for Password Generation

When developers need to generate random values for passwords, tokens, or cryptographic keys, the first instinct is often to reach for Math.random(). It is available in every language, requires no imports, and seems to produce random-looking output. But Math.random() is not cryptographically secure, and using it for password generation is a dangerous anti-pattern that can compromise your entire security posture. Always use a secure password manager like NordPass that relies on cryptographically secure random number generation.

The Fundamental Problem: Predictability

Math.random() (and its equivalents across languages) uses a pseudo-random number generator (PRNG) seeded from the system clock or a similar low-entropy source. Given enough observed outputs, an attacker can reconstruct the internal state and predict every subsequent "random" value. This is not theoretical — it is a well-documented vulnerability.

// DANGEROUS — Never use this for passwords!
function generatePassword(length) {
    const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    let password = "";
    for (let i = 0; i < length; i++) {
        password += chars[Math.floor(Math.random() * chars.length)];
    }
    return password;
}

The problem: Math.random() in V8 (Chrome/Node.js) uses the xorshift128+ algorithm, which can have its entire state reconstructed after observing approximately 65 outputs. An attacker who captures a handful of generated passwords can predict every password you will generate in the future.

CSPRNG: The Only Acceptable Alternative

A Cryptographically Secure Pseudo-Random Number Generator (CSPRNG) derives its randomness from operating-system-level entropy sources that cannot be predicted or reconstructed. Every major platform provides a CSPRNG API.

Node.js:

const crypto = require('crypto');

function generateSecurePassword(length = 16) {
    const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
    const bytes = crypto.randomBytes(length);
    let password = "";
    for (let i = 0; i < length; i++) {
        password += chars[bytes[i] % chars.length];
    }
    return password;
}

Python:

import secrets
import string

def generate_secure_password(length=16):
    chars = string.ascii_letters + string.digits + "!@#$%^&*"
    return ''.join(secrets.choice(chars) for _ in range(length))

Go:

import (
    "crypto/rand"
    "math/big"
)

func GenerateSecurePassword(length int) string {
    chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
    result := make([]byte, length)
    for i := range result {
        n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
        result[i] = chars[n.Int64()]
    }
    return string(result)
}

What the Standards Say

Testing Your Randomness

If you are unsure whether your random number generator is secure, test it against these criteria:

  1. Seed uniqueness: Does it seed from high-entropy sources (/dev/urandom, CryptGenRandom)?
  2. Period length: Is the period so large it will never repeat in practical use?
  3. State compromise resistance: Can an attacker reconstruct internal state from outputs?
  4. Forward secrecy: If state is compromised, can past outputs be reconstructed?

Math.random() fails all four tests. A CSPRNG passes all four.

The Bottom Line

Math.random() is fine for shuffling UI elements or generating random IDs for non-security purposes. But for passwords, API keys, session tokens, or any credential, it is never acceptable. Use your platform's CSPRNG — it is just as easy to use and infinitely more secure.

Generate a Free Strong Password →

More Password Security Tools

🔑 SecureKeyGen⚔️ TitanPasswords🛡️ Best Password Generator🔐 Free Strong Password⚡ Instant Password🗝️ Iron Vault Keys👨‍👩‍👧‍👦 Safe Pass Builder🛡️ Trusty Password⚙️ StrongPassFactory🔑 SecureKeyGen.org📚 TrustyPassword.org

Why Math.random() Is Never Acceptable for Password Generation

Generating passwords seems deceptively simple: pick some random characters and string them together. Many developers reach instinctively for JavaScript's Math.random() to do the job. Unfortunately, this function is fundamentally unsuitable for any security-sensitive purpose, and using it to generate passwords introduces a silent, dangerous vulnerability into your application. The problem is not that Math.random() produces poor-looking output—its results appear perfectly scrambled to the human eye—but that the randomness is mathematically predictable to anyone who understands how it works.

The Core Problem: Predictability

At its heart, Math.random() is a pseudo-random number generator (PRNG). It uses a deterministic algorithm, typically a variant of the xorshift family in modern engines like V8, seeded from an internal state. Because the algorithm is deterministic, every number it produces is a direct mathematical consequence of the numbers that came before it. This means the output stream is not truly random at all—it merely appears random. A security-grade generator must be unpredictable even to an attacker who has observed previous outputs. Math.random() fails this test completely.

How an Attack Unfolds

Imagine a web application that generates temporary passwords or reset tokens using Math.random(). If an attacker can trigger or observe even a few generated values—perhaps through a public-facing feature that exposes random IDs—they can feed those values into a solver that recovers the PRNG state. Once recovered, the attacker can predict the next password the system will issue, including a victim's account recovery token. What looks like a brute-force problem of astronomical difficulty collapses into a trivial computation. The password's apparent length and complexity become irrelevant because the attacker never has to guess; they simply calculate.

The Correct Approach

Secure password generation requires a cryptographically secure pseudo-random number generator (CSPRNG), which draws entropy from the operating system and is specifically designed to be unpredictable. Every major platform provides one.

When mapping random bytes onto a character set, take care to avoid modulo bias by rejecting values that fall outside an evenly divisible range. This ensures every character in your alphabet is equally likely, preserving the full entropy of the source.

The Bottom Line

The distinction between Math.random() and a CSPRNG is not a matter of preference, optimization, or paranoia—it is the difference between a password that is genuinely unguessable and one that merely pretends to be. Because the cost of switching to a secure generator is essentially zero, and the cost of a predictable password is a full account compromise, there is no scenario in which Math.random() is an acceptable choice for generating passwords. Treat it as a tool for shuffling animations and dice rolls, never for protecting secrets.

We use cookies to improve your experience. Learn more