Cryptographically Secure Random Numbers Explained: Why Password Generators Don't Use Math.random()

SP
Sreehari Pradeep
July 12, 202612 min read

Cryptographically Secure Random Numbers Explained: Why Password Generators Don't Use Math.random(). If you've ever generated a password online, you probably assumed that the characters were chosen completely at random. But behind that "Generate" button lies a complex system of cryptographic algorithms, hardware noise harvesting, and statistical sampling. In the world of cybersecurity, not all randomness is created equal.

Most programming languages offer simple, built-in functions to generate random numbers, such as JavaScript's Math.random(). While these functions are perfect for simple games and visual effects, they are highly insecure and can be easily cracked by attackers. Understanding why password generators cannot use Math.random() requires looking deep into how computers handle predictability, secrecy, and entropy.

This deep-dive guide will walk you through the physics and mathematics of cryptographic randomness. We will explain how operating systems capture physical uncertainty, how browsers translate that uncertainty into cryptographically secure values, and how high-quality password generators eliminate statistical bias to ensure that every password is truly unbreakable.

Before We Begin: Every Secure Password Starts Here

When you click "generate password" on a website, the browser must produce a sequence of characters that an attacker cannot guess. But how do we define "unpredictability"? If you roll a standard six-sided die, the result is random to you because you cannot calculate the force of your throw, the air resistance, or the friction of the table. However, if a high-speed camera and a physics engine measured those variables, they could predict the roll before the die stopped. Password security requires a level of randomness that remains unpredictable even if the attacker knows everything about the machine rolling the die.

To establish this security, modern browsers do not rely on standard programming functions. Instead, they make direct requests to the operating system's kernel, which acts as a central reservoir of uncertainty. This division of labor ensures that your security keys and passwords are rooted in physical chaos rather than digital logic.

Metric Human-Created Password CSPRNG-Generated Password
Method Memory, common patterns, keyboard paths Web Crypto API, OS kernel entropy
Predictability High (vulnerable to dictionary & hybrid attacks) Zero (statistically uniform distribution)
Entropy (per character) Low (often under 2-3 bits) High (up to 6.15 bits per character)
Attack Resistance Weak (cracked in milliseconds by modern GPU arrays) Extremely High (computationally infeasible to brute-force)

How Secure Randomness Reaches Your Browser

Physical World (Timing Variations)
Operating System Entropy Collection
Entropy Pool
Kernel CSPRNG
Web Crypto API
crypto.getRandomValues()
Password Generator
Secure Password

In 2013, researchers analyzed millions of passwords leaked from breaches and found that even when users were instructed to create "complex" passwords, over 30% of them could be cracked instantly using dictionaries modified with simple rules (like replacing 'a' with '@'). In contrast, passwords generated using standard OS-level CSPRNGs have never been cracked by guessing the random sequence.

Key Takeaway

True password security is impossible without cryptographically secure randomness, which replaces human bias with mathematical unpredictability.

Before we can understand how browsers harness this mathematical unpredictability, we must first confront a fundamental paradox of computing: how can a device built for absolute logic generate random numbers in the first place?

Can Computers Actually Generate Random Numbers?

Computers are machines of absolute precision. If you ask a processor to calculate 2 + 2, it will always return 4. If you run the same program ten times, it executes the same instructions in the exact same sequence. How can a machine that is completely deterministic produce a random number?

Imagine a mechanical clock. If you know the positions of all the gears and the tension in the spring, you can calculate exactly when the clock will strike the hour. A computer is like that clock, just much faster and more complex. If you give a program the same starting inputs, it will produce the same outputs. To get something random, the computer has to reach outside its mechanical gears and observe the messy, unpredictable physical world.

What Does "Deterministic" Mean?

In simple terms, a deterministic system is like a calculator. If you type 2 + 2, it will always output 4. It cannot decide to output 5 today just because it feels like it.

Every program, from a simple spreadsheet to a complex video game, is deterministic. Given the exact same starting instructions and inputs, it will step through the exact same path and produce the exact same result. In fact, every single transistor inside a computer chip is designed to act deterministically. If a computer component behaves unpredictably, engineers call it a hardware failure.

Because computers are designed to be this logical, they cannot create actual randomness out of thin air. If you want a computer to generate a random number, you have to feed it something from the outside world that is already unpredictable.

Why This Becomes a Security Problem

If a password generator uses a deterministic system without external uncertainty, an attacker who replicates the initial inputs can generate the exact same passwords. This eliminates the secrecy of the password. For example, if a program uses the current hour as its only input, an attacker only has to test 24 possible password combinations to find yours.

A Different Kind of Random

To solve this, computer scientists divide randomness into two classes: pseudorandomness (generated by mathematical formulas starting from an initial value called a seed) and true randomness (derived from physical measurements of physical systems). While true randomness is ideal, it is too slow to generate in large quantities, so modern systems combine both approaches.

Computers Borrow Randomness From Reality

Modern operating systems continuously gather physical noise from device drivers: the exact microsecond intervals between keypresses, the sub-millimeter jitter of your mouse, the seek times of solid-state drives, and the arrival times of network packets. These external events are converted into binary numbers and fed into the kernel's entropy pool, providing the computer with a source of genuine, unpredictable physical uncertainty.

Random Doesn't Mean "Looks Random"

A common mistake is evaluating randomness by eye. A sequence like 1, 2, 3, 4 might be generated by a secure, fair roll of a die, while a sequence like X9f!Lp2 might be generated by a simple, repeating mathematical pattern. True randomness is about the process that created the numbers—specifically, whether that process can be predicted by an outsider—not how the result looks.

Deterministic Doesn't Mean Insecure

Although a cryptographic random generator uses deterministic mathematics to expand its state, it is secure because its starting seed is kept completely secret and is continuously mixed with new entropy. This ensures that even if an attacker knows the algorithm, they cannot compute the seed, making the output indistinguishable from true physical randomness.

Why Computers Need Entropy

Deterministic Computing
SAME INPUT
SAME PROGRAM
SAME INTERNAL STATE
SAME OUTPUT

Without external inputs, the system is fully predictable.

Entropy-Powered Cryptography
Keyboard Timing
Mouse Movement
SSD Timings
Network Packets
OPERATING SYSTEM
🔒 KERNEL CSPRNG
SECURE RANDOM OUTPUT

Real-world chaos updates the state, making output unpredictable.

Computers don't create randomness from nothing. They securely transform unpredictable events from the physical world into cryptographically secure random data.

In the early days of online poker (1999), a popular site generated random decks using the system clock's millisecond counter. Because players could synchronize their own computers to the server's clock, they could calculate the exact seed and predict the entire deck in real-time, winning thousands of dollars.

Key Takeaway

Computers cannot manufacture true randomness from pure logic; they must capture physical uncertainty (entropy) and use it as a foundation for security.

To convert these captured signals of physical chaos into usable numbers, computers rely on specialized systems. Let's look at the different members of the random number generator family.

What Is a Random Number Generator (RNG)?

We often hear about "random number generators" in games, encryption, and password tools. But are they all the same? If a game's random number generator decides which treasure drops from a chest, can we use that same generator to secure a bank account?

Think of random number generators like vehicles. A go-kart, a sedan, a tractor, and a military tank are all vehicles, but you wouldn't drive a go-kart on a battlefield or use a tank to pick up groceries. Similarly, different types of random number generators are built for different jobs. A generator that is fast enough for a video game may be completely vulnerable to hackers.

The Four Main Types

TRNG (True Random Number Generator): Measures physical processes (quantum mechanics, atmospheric noise) directly. Extremely unpredictable but slow.

HRNG (Hardware Random Number Generator): Dedicated silicon circuits on modern processors that measure microscopic thermal noise. Very secure, used to seed system entropy.

PRNG (Pseudorandom Number Generator): Mathematical formulas that generate long sequences of numbers very quickly. Good for games, insecure for cryptography.

CSPRNG (Cryptographically Secure PRNG): Advanced algorithms that combine secret seeds with cryptographic operations, ensuring outputs cannot be predicted even if previous values are known.

RNG Class Primary Source Speed Predictability Best Use Case
TRNG Quantum/Atmospheric Noise Slow Completely Unpredictable High-level seeding, physics research
HRNG CPU Junction Noise Medium-Fast Completely Unpredictable Kernel entropy injection
PRNG Math equations (e.g., LCG) Extremely Fast Predictable (given state) Video games, simulations
CSPRNG Crypto algorithms (AES, ChaCha) Fast Unpredictable (cryptographically) Passwords, keys, session tokens

The Random Number Generator Family

Random Number Generators (RNGs)
🌌 TRNG True Random Number Generator Source: Physical phenomena (quantum, thermal noise) Use Case: Seeding CSPRNGs Security: High
🔌 HRNG Hardware Random Number Generator Source: CPU-level hardware (Intel RDRAND, AMD) Use Case: System entropy feed Security: High
🎮 PRNG Pseudorandom Number Generator Source: Mathematical seed + formulas Use Case: Games, physics simulations Security: Insecure
🔒 CSPRNG Cryptographically Secure PRNG Source: Cryptographic algorithms + entropy Use Case: Passwords, encryption, SSL Security: Cryptographic

Intel's modern processors include a hardware instruction called RDRAND, which reads random values directly from a built-in HRNG. This HRNG is used by the operating system kernel to mix high-quality randomness into its security pools.

Key Takeaway

While ordinary PRNGs prioritize execution speed, security applications require CSPRNGs that prioritize resistance to prediction.

To see why security applications reject ordinary generators, we must look closer at the inner workings of a basic Pseudorandom Number Generator (PRNG) and the concept of "the seed."

What Is a Pseudorandom Number Generator (PRNG)?

If you have ever run a simple coding script to generate random numbers, you might have noticed a strange quirk: if you run it multiple times, it might produce the exact same sequence. Or, if you run it on two different machines with the same setup, the results might match perfectly. Why does this happen?

It happens because the computer is not actually generating new numbers on the fly. Instead, it is using a Pseudorandom Number Generator (PRNG), which acts like a massive, pre-printed book containing millions of random-looking digits. If two people open the exact same book to page 42 and read the digits from top to bottom, they will read the exact same sequence. In this analogy, the book is the PRNG algorithm, and page 42 is the "seed" (the starting point). If you know the page number, the future is completely predictable.

Understanding the Seed

A seed is an initial value (an integer or byte array) that initializes the state of a PRNG. Once the seed is set, the PRNG's mathematical formula produces a deterministic sequence of numbers. If you use the same seed, the formula will produce the exact same sequence every single time.

A Concrete Example: The Modulo Formula

To see how deterministic formulas work in practice, let's look at a simple math formula often used to introduce programming students to pseudorandomness:

Next Number = (Previous Number * 3 + 1) % 10

Here, the modulo operator (% 10) means "divide by 10 and keep only the remainder." Let's see what happens if we choose a starting "seed" of 2 and run the formula repeatedly:

  • First Run: We start with our seed, 2. We multiply it by 3 and add 1 to get 7. The remainder of 7 divided by 10 is 7.
  • Second Run: We feed 7 back into the formula. (7 * 3 + 1) = 22. The remainder of 22 divided by 10 is 2.
  • Third Run: We feed 2 back in. (2 * 3 + 1) = 7. The remainder is 7.
  • Fourth Run: We feed 7 back in. (7 * 3 + 1) = 22. The remainder is 2.

This simple formula generates the sequence: 7, 2, 7, 2, 7, 2....

While this is an extremely basic PRNG, it illustrates the three core truths of pseudorandom number generators:

  1. It looks random-ish: To someone who doesn't know the formula, the numbers jump back and forth.
  2. It is completely deterministic: If you start with the seed 2 again, you will get the exact same sequence 7, 2, 7, 2 every single time.
  3. It is easily solvable: If an attacker observes that the generator just output the number 7, they can calculate that the next number will be 2. There is no true secrecy.

Why Predictability Can Be Useful

In games or simulations, using the same seed allows players to share identical game worlds (like a Minecraft world seed). In scientific computing, it allows researchers to reproduce exact test runs to verify results. If a researcher is simulating a weather system, they want to be able to run the simulation again with the exact same variables to isolate causes.

Common Uses of PRNGs

PRNGs are found in every standard programming library because they are computationally cheap and fast. Examples of common algorithms include Linear Congruential Generators (LCG) and the Mersenne Twister. They are widely used in video games for enemy spawns, particle effects, and procedural generation, as well as in statistical analysis and Monte Carlo simulations.

Why PRNGs Fail in Security

PRNGs have a relatively small internal state. If an attacker observes a short sequence of outputs, they can run simple algebra to determine the current internal state and predict every future number. Since many default PRNGs seed themselves using the current system epoch time, an attacker who knows the approximate time of generation can brute-force the seed in milliseconds.

Looking Random Isn't Enough

A PRNG can produce a sequence of numbers that passes every statistical test for uniformity—meaning all digits occur with equal frequency—yet remain completely insecure. Statistical randomness is not cryptographic randomness. Security requires that even if you show an attacker a million outputs, they have no better than a 50% chance of guessing the next digit.

A Common Misconception

Many developers think they can make a PRNG secure by adding complex mathematical equations on top of the output. This is incorrect. Because the source of the numbers is deterministic and has low state entropy, any transformations applied to the output are also deterministic and can be mapped by an attacker.

The Seed Determines Everything

Seed = 42
PRNG Algorithm
53 91 17 44
Seed = 42
PRNG Algorithm
53 91 17 44

Same seed + same algorithm = identical output. There is no actual chance or secrecy here.

PRNG: Speed vs. Security

✓ Repeatability & Speed Excellent For:
  • Video game world seeds (Minecraft, Terraria)
  • Procedural computer graphics & animations
  • Scientific & physics Monte Carlo simulations
  • Reproducible software unit tests
⚠ Predictability Vulnerability Dangerous For:
  • Passwords & reset tokens
  • Session identifiers & cookies
  • API access keys
  • SSL/TLS certificates & digital signatures
⚠ Never use an ordinary PRNG for cryptographic security.

Common Misconception

Many believe that a password generated by a PRNG is safe because the seed is unknown. However, seeds are often small (like the system epoch time in milliseconds), meaning an attacker can perform a brute-force search over all possible millisecond seeds within a target window in less than a second.

The PHP function rand() was traditionally a basic LCG. When used to generate password reset tokens, attackers calculated the system clock time of the request and successfully guessed the tokens, hijacking user accounts.

Key Takeaway

An ordinary PRNG is a mathematical illusion of randomness, easily cracked once its seed or a small sample of output is compromised.

To prevent attackers from predicting numbers, cryptographers designed a far more secure class of generators that protect their internal states: Cryptographically Secure Pseudorandom Number Generators (CSPRNGs).

What Is a Cryptographically Secure Pseudorandom Number Generator (CSPRNG)?

How does a bank generate a temporary security code that cannot be predicted, even if a hacker knows the exact algorithm the bank uses? How can an algorithm remain secure when its source code is completely public?

Imagine a black box with a slit at the top and a crank on the side. You drop a secret slip of paper (the seed) into the slit, and turn the crank. Unpredictable numbers slide out. Even if a hacker holds the exact same black box, they cannot predict what comes out of yours unless they know the exact paper you dropped in. More importantly, even if they see the numbers sliding out, they cannot look inside the box to figure out what was written on your paper.

The Goal Isn't True Randomness

A CSPRNG doesn't actually produce true physical randomness. Because it runs on a computer, it still uses mathematical formulas under the hood. However, it is designed so that the output is mathematically indistinguishable from true randomness. Even if a hacker uses a supercomputer to analyze millions of generated numbers, they will find absolutely no patterns or telltale signs to help them guess the next digit. The math is a one-way street: easy to run forward, but impossible to reverse.

Security by Design Not Secrecy

According to Kerckhoffs's Principle, a cryptographic system must remain secure even if everything about it is public, except for the key (or seed). A CSPRNG does not hide its code. It relies on mathematical puzzles (like the difficulty of reversing block ciphers) to protect its output. This means that even if a hacker knows the exact formula your system uses, your passwords remain perfectly secure.

The Three Core Security Goals

To earn the title of "cryptographically secure," an algorithm must pass three strict tests:

  • The Next-Bit Test (No Predictions): If a hacker watches the generator output a sequence of bits (like 1101001...), they still have a maximum 50% chance (no better than a coin flip) of guessing whether the next bit will be a 0 or a 1.
  • Backtracking Resistance (No Past History): If an attacker breaks into a server and discovers the generator's current internal state at 5:00 PM, they still cannot use that information to calculate what passwords or security keys were generated at 4:59 PM. The past is permanently locked.
  • High-Quality Seeding: The starting seed must be completely random and unguessable, built using a full 256 bits or more of high-quality entropy collected from hardware timing fluctuations.

How a CSPRNG Works

It uses a strong cryptographic primitive (like the AES block cipher, the ChaCha20 stream cipher, or hash-based algorithms like Hash_DRBG) in a feedback loop. The secret state is encrypted, producing random output blocks, and the state is modified cryptographically after each block. This ensures that the internal variables change continuously and unpredictably.

How a CSPRNG Evolves State

🌀
Physical Entropy
OS events pool
🔒 INTERNAL STATE (Constantly Evolving)
🎲
Random Output
Cryptographically secure
State Update loop: Generating random bytes triggers cryptographic mixing that updates the internal state. Even if an attacker learns the state today, they cannot determine past random bytes (backtracking resistance) nor future ones after new entropy enters.

Security Insight

Backtracking resistance is critical. If a server generating session keys is temporarily compromised, backtracking resistance ensures the attacker cannot decrypt previous sessions using the compromised state.

Modern operating systems implement CSPRNGs at the kernel level: /dev/urandom in Linux and macOS, and BCryptGenRandom in Windows. These systems use cryptographic primitives to expand a seed into billions of secure random bytes.

Key Takeaway

A CSPRNG protects its internal state using cryptographic algorithms, making it mathematically impossible to predict future values from past outputs.

But a CSPRNG's cryptographic strength is only as good as the raw unpredictability it is initialized with. This brings us to the core of security: entropy.

What Is Entropy? The Foundation of Every Secure Random Number

Why do security experts talk about "entropy" as if it were a physical resource like oil or electricity? Why does your computer need to collect entropy, and what happens if it runs out?

Imagine trying to flip a coin that you know is perfectly balanced. The outcome is 50/50. That coin flip contains exactly 1 "bit" of unpredictability. Now imagine a coin that is weighted so it lands on heads 99% of the time. Flipping that weighted coin provides almost no surprise—it has very low entropy. In cybersecurity, entropy is the measure of surprise. If there is no surprise, there is no security.

A Simple Way to Think About Entropy

In computer science, we use the term "entropy" to measure how much uncertainty a password or key holds. If you are choosing a password from a set of options, the math is simple: the more options you have, and the more equal the chance of picking each option, the higher the entropy. More entropy means a larger search space for an attacker, making their guessing games mathematically impossible.

Why Cryptographers Measure Entropy in Bits

A secret with k bits of entropy requires an attacker to perform up to 2^k (2 multiplied by itself k times) guesses on average to brute-force it. Measuring entropy in bits allows cryptographers to calculate exactly how many physical guesses it would take to crack a password.

Let's look at what these "bits of entropy" mean in real-world, concrete numbers:

  • 1 Bit of Entropy: Equal to a single coin flip. There are only 2 possibilities (Heads or Tails). An attacker needs a maximum of 2 guesses.
  • 4 Bits of Entropy: Equal to 4 coin flips. There are 2^4 = 16 possibilities. An attacker needs a maximum of 16 guesses.
  • 20 Bits of Entropy: Equal to a standard 6-digit PIN (which has 1 million combinations, or about 19.9 bits). A budget smartphone can guess all 1 million combinations in 0.01 seconds.
  • 128 Bits of Entropy: Standard for modern cryptographic keys. There are 2^128 possibilities, which is approximately 340 undecillion (340 followed by 36 zeros). Even if all the computers on Earth spent billions of years guessing, they could not test even a fraction of this space.

How to Calculate Password Entropy

We calculate a password's entropy using the formula: E = L * log2(R). This looks complicated, but it is actually very simple if we break down what the letters mean:

  • L is the length of the password (the number of characters in it).
  • R is the pool size (the number of characters we can choose from, like 10 digits or 26 lowercase letters).
  • log2(R) is the entropy of a single character. It asks: "How many coin flips is a single character from this pool worth?"

For example, if you choose characters from a pool of 71 letters, numbers, and symbols, each character is worth about 6.15 coin flips. If you generate a 16-character password, the total entropy is: 16 * 6.15 = 98.4 bits of entropy. This means guessing your password is exactly as hard as trying to guess the outcome of 98 coin flips in a row!

Password Length Pool Size Entropy (Bits) Guesses Required Security Status
8 characters 10 (Digits) 26.6 bits 10^8 Insecure (cracked instantly)
12 characters 62 (Alphanumeric) 71.4 bits 3.2 x 10^21 Moderate (susceptible to corporate-scale clusters)
16 characters 71 (Custom Pool) 98.4 bits 4.7 x 10^29 Strong (secure for standard accounts)
20 characters 71 (Custom Pool) 123.0 bits 1.5 x 10^37 Exceptional (military-grade, proof against future clusters)

Continuous Entropy Harvesting

Keyboard Timings
🖱 Mouse Movements
💾 Disk Access Timings
🌐 Network Packets
CPU Clock Jitter
🌊 ENTROPY POOL A continuously updated, mixed reservoir of hardware uncertainty.

When booting up a new virtual machine in a cloud environment, it may lack physical peripherals (like a mouse or keyboard) to harvest entropy. If it generates encryption keys immediately on boot, those keys may have very low entropy and be easily cracked. This is known as the "boot-time entropy starvation" problem.

Key Takeaway

Entropy measures the actual unpredictable surprise of a value, and it is the raw resource that fuels all cryptographic security.

Now that we understand entropy, let's explore how modern web browsers make this operating-system-level entropy available to web applications.

How Browsers Generate Secure Random Numbers

How does a website running inside a sandboxed browser tab access the cryptographically secure random numbers managed by the operating system kernel?

Imagine the browser as a secure office building, and the operating system as the vault in the basement. The website is a guest in one of the offices. The website isn't allowed to go down to the basement themselves, but they can use a dumbwaiter (the Web Crypto API) to send a request down to the vault and receive a secure package of random bytes.

What Is the Web Crypto API?

The Web Crypto API is an interface allowing web applications to access cryptographic primitives (such as hashing, signature generation, encryption, and secure random value generation) in a safe, standard way. It was introduced to eliminate the need for websites to write their own custom, insecure crypto code in JavaScript.

The Role of crypto.getRandomValues()

The crypto.getRandomValues(array) method is the secure bridge. When a web developer writes JavaScript code to generate a password, they do not calculate random numbers using JavaScript's own math engines. Instead, they create a blank array of numbers and pass it to this method. The browser immediately pauses, reaches outside of its sandbox, and asks the operating system's kernel to fill that blank array with fresh, high-quality random bytes directly from the hardware-backed entropy pool.

What Actually Happens When You Click "Generate Password"?

When you use a high-quality local password generator, the JavaScript code allocates a block of memory (an array of bytes) and passes it to window.crypto.getRandomValues(). The browser stops execution briefly, requests secure bytes from the OS kernel, receives them, and puts them into the array. The generator then processes these secure bytes to build your password.

The Path of Client-Side Generation

1. User Clicks Generate Browser
2. Password Generator Code calls API JS Logic
3. crypto.getRandomValues() Web Crypto API
4. OS Cryptographic System Kernel CSPRNG
5. Pulls bytes derived from Entropy Pool OS Kernel
6. Secure Random Bytes returned Browser Context
7. Map bytes to chars (Rejection sampling) Password Created

Modern browsers like Chrome, Firefox, Safari, and Edge all implement the Web Crypto API. Under the hood, Chrome delegates to BoringSSL (Google's cryptography library), which queries /dev/urandom on macOS/Linux and BCryptGenRandom on Windows.

Key Takeaway

The Web Crypto API provides a secure bridge between client-side JavaScript and the operating system's hardware-backed entropy pool.

If the Web Crypto API is so secure, why did developers spend years using Math.random(), and why is that old habit now considered a critical security vulnerability?

Why Math.random() Should Never Be Used for Password Generation

If you search the internet for "how to generate a random number in JavaScript," the first result is almost always Math.random(). If it's the standard way to get random numbers, why will a security scanner flag it as a high-severity vulnerability if you use it for password generation?

Imagine a magician who does card tricks. To a spectator, the cards appear to be chosen completely at random. But another magician sitting in the audience knows the exact deck order and the sleight-of-hand formula. They can predict every card the magician will pull. Math.random() is like that magician—it looks random to the user, but its logic is fully visible to anyone who understands the math formula running under the hood.

What Math.random() Was Designed For

Math.random() was designed for speed and simplicity. Its goal is to produce numbers between 0 and 1 with a uniform statistical distribution. It is optimized for applications like canvas animations, non-critical games, and UI layout spacing. It was never intended to defend against active adversaries.

Why Looking Random Isn't Enough

A generator can pass statistical tests for uniform distribution (meaning every number appears equally often) while being completely predictable. An attacker doesn't need the numbers to look biased; they just need to find the equation that connects them.

The Problem With Predictable Internal State

Most browsers implement Math.random() using an algorithm called xorshift128+. This algorithm maintains a 128-bit internal memory (its state). Think of this state like a gear with 128 teeth. Every time you ask for a random number, the gear turns, shifts its teeth left and right, and spits out a number. Because this shifting formula is relatively simple, if an attacker observes just two consecutive numbers generated by Math.random(), they can use basic algebra to figure out the exact position of the gear. Once they know that, they can predict every single random number your browser will generate next.

Feature Math.random() crypto.getRandomValues()
Design Goal Execution speed, statistical uniformity Cryptographic unpredictability, state protection
Underlying Engine Browser JS engine (e.g., xorshift128+) OS Kernel CSPRNG (e.g., ChaCha20, AES)
Predictability Predictable after observing ~2-5 outputs Computationally impossible to predict
Source of Entropy Low-quality seed (typically system clock) High-quality physical hardware events
Security Audit Fails (flags vulnerability) Passes (standard for secure apps)

Math.random() vs. crypto.getRandomValues()

Math.random() General Programming
• Implemented by JavaScript engine
• Designed for execution speed
• Yields statistical randomness
• State is easily recoverable
NOT FOR SECURITY
crypto.getRandomValues() Web Crypto API
• Delegates to Operating System
• Designed for cryptographic secrecy
• Yields secure entropy streams
• Unpredictable state evolution
🛡
DESIGNED FOR SECURITY

In 2016, security researchers demonstrated that they could predict session cookies generated by a popular Node.js framework because it used Math.random(). By capturing a few public session IDs, they predicted the next ID and hijacked active user sessions.

Key Takeaway

Using Math.random() for security is like putting a screen door on a submarine—it was never designed to hold up under pressure.

But using a CSPRNG is only the first step. Even with secure random bytes, a password generator can introduce subtle mathematical biases if it maps those bytes to characters incorrectly. Let's look at a common mistake known as modulo bias.

Why High-Quality Password Generators Use Rejection Sampling

If you have a perfectly fair 6-sided die, how do you use it to choose a number between 0 and 3 (4 options) with exactly equal probability? A common programmer shortcut is to roll the die and find the remainder after dividing the result by 4 (using the modulo operator). But does this give every number an equal chance?

Let's look at the math for each of the 6 possible die rolls:

  • Roll a 1: 1 divided by 4 leaves a remainder of 1.
  • Roll a 2: 2 divided by 4 leaves a remainder of 2.
  • Roll a 3: 3 divided by 4 leaves a remainder of 3.
  • Roll a 4: 4 divided by 4 leaves a remainder of 0.
  • Roll a 5: 5 divided by 4 leaves a remainder of 1.
  • Roll a 6: 6 divided by 4 leaves a remainder of 2.

Let's count how many times each final remainder option appears:

  • Result 0: Appears 1 time (only when rolling a 4)
  • Result 1: Appears 2 times (when rolling a 1 or 5)
  • Result 2: Appears 2 times (when rolling a 2 or 6)
  • Result 3: Appears 1 time (only when rolling a 3)

Because the numbers 1 and 2 can be produced by two different die rolls, they are twice as likely to appear as 0 and 3. In a game, this might seem minor. But in password security, if an attacker knows that certain characters are 33% more likely to be chosen than others, they can program their cracking software to guess those biased characters first, drastically reducing the time it takes to break your password. This statistical inequality is called modulo bias.

The Hidden Problem

Modulo bias occurs when a random number range (e.g. 0 to 255 from a random byte) is mapped to a smaller pool size N using the modulo operator (%), and N does not divide the range size evenly. This causes the first few characters in the pool to have a slightly higher chance of selection, degrading the overall security of the password.

Why Modulo Creates Bias

Let's say we have 10 possible random values (0 to 9) and we want to map them to 4 characters (A, B, C, D). If we use modulo 4, the mapping looks like this:

Random Value Modulo 4 Value Mapped Character
0, 4, 8 0 A (3 possible inputs)
1, 5, 9 1 B (3 possible inputs)
2, 6 2 C (2 possible inputs)
3, 7 3 D (2 possible inputs)

This means characters A and B have a 30% chance of appearing, while C and D only have a 20% chance. An attacker who knows this bias can crack the password faster by prioritizing biased characters. In cryptographic sizes, mapping 256 byte values to a pool of 71 characters means the first 43 characters are 33% more likely to be chosen than the remaining 28 characters.

Why Modulo Bias Happens

256 Random Bytes divided among 71 Characters
Characters 0 → 42 Each maps to 4 possible byte values.
Characters 43 → 70 Each maps to only 3 possible byte values.
Result: The first 43 characters are 33% more likely to be chosen!

The Solution: Rejection Sampling

To fix modulo bias, secure password generators use a technique called rejection sampling. In simple terms: if the math isn't perfectly fair, we throw the number away and try again.

Let's see how this works using our 71-character password pool and a single random byte (which gives us a number from 0 to 255):

  1. Find the highest fair multiple: The largest multiple of 71 that fits inside 256 is 213 (which is 71 * 3).
  2. Set a cutoff line: If our generator gets any random number from 0 to 212, we keep it because 213 divides perfectly by 71. Every character in our pool gets exactly 3 possible byte matches.
  3. Reject the unfair numbers: If our generator gets a number from 213 to 255, we throw it away. We do not try to fix it or squeeze it in. We simply reject it, make another request to the Web Crypto API, and get a new byte.

By throwing away the remainder values at the tail end, we ensure that every character in the pool has exactly the same probability of being chosen. No character has an unfair advantage.

Rejection Sampling in Action

Accepted Path
Byte Value: 188
Is inside unbiased range (0 → 212)?
YES (Accept)
188 % 71 = Index 46
Discarded Path
Byte Value: 241
Is inside unbiased range (0 → 212)?
NO (Discard & Loop)
Request next secure byte

Key Takeaway

Rejection sampling ensures statistical perfection, eliminating modulo bias by discarding values that would give certain characters an unfair advantage.

Eliminating bias at the character level is vital, but what about the overall structure of the password? If we enforce complexity rules by swapping characters after generating them, do we break the randomness? Let's look at full-string rejection.

Why High-Quality Password Generators Use Full-String Rejection

Many websites require your password to contain at least one uppercase letter, one lowercase letter, one number, and one symbol. If a password generator creates a password and then forces a number into the 5th position to meet the requirement, does it make the password stronger or weaker?

Imagine a lottery drawing where 6 numbers are pulled from a machine. If the rules suddenly state "there must be at least one odd number," and if the draw has only even numbers, the host simply grabs the 3rd ball and replaces it with an odd ball. Does that drawing still feel completely fair and random? No, because you've altered the probability space. To keep the lottery completely fair, the host should discard the entire draw and run the machine again from scratch.

A Concrete Example: Generating a 4-Character Password

Let's say we want a password of 4 characters that must contain at least one lowercase letter, one uppercase letter, one number, and one symbol (like a, B, 3, and !).

  • The Bad Way (Template Swap): The generator randomly picks lowercase letters first: x, y, and z. Realizing it lacks a symbol, a capital letter, and a number, it forces those into the final positions, returning: x B 3 !. Because the capital, number, and symbol are always forced into the 2nd, 3rd, and 4th positions, a hacker's script only has to guess the first letter. The entropy of the other three slots drops to zero.
  • The Secure Way (Full-String Rejection): The generator picks 4 characters completely independently: x, y, z, and w. It checks if the string has a symbol, capital letter, and number. It does not, so it discards the entire candidate xyzw. It starts over from scratch, generating K, p, 2, and #. This candidate matches the rules, so it is kept: Kp2#. In this password, the symbol and number could be in any of the 4 slots, preserving maximum unpredictability for every single position.

Unbiased Policy Validation

1. Generate complete 12-char candidate password
(Each position selected independently from complete pool)
2. Validate against policy requirements
(Has uppercase, lowercase, number, symbol?)
✓ YES (Matches Policy) Deliver password to clipboard. SUCCESS
✗ NO (Misses Policy) Discard entire string. Regenerate from Step 1

Key Takeaway

Full-string rejection guarantees that every character in the final password is the result of an unbiased, independent choice, preserving maximum complexity.

Now that we have a secure, unbiased method for generating passwords, we must address the final parameter: how long should the generated password actually be?

How Long Should a Password Be?

If you increase a password's length from 12 characters to 16 characters, does it make it 33% harder to crack because it is 33% longer? Or is the difference much larger?

Think of password length like steps on an exponential staircase. Each step doesn't just add a small height; it multiplies the total height of the staircase. Moving from 12 to 16 characters doesn't make the password 33% stronger—it makes it billions of times stronger, turning a password that could be cracked by a state-level computer cluster into one that would take longer than the age of the universe to guess.

Why Length Matters More Than Complexity

Because entropy grows exponentially with length (E = L * log2(R)), adding characters increases the keyspace far more than adding symbol variety. A 20-character password made only of lowercase letters is significantly harder to crack than a 10-character password packed with complex symbols.

Every Additional Character Makes a Huge Difference

When you add one character to a password, you multiply the entire search space by the size of the character pool. If you use a pool of 71 characters, adding just 4 characters makes the password 25 million times harder to crack than it was before:

Exponential Growth of Password Complexity

16 Characters
Base keyspace (1x)
17 Characters
71x larger
18 Characters
5,041x larger
19 Characters
357,911x larger
20 Characters
25.4 million x larger!

Each additional character multiplies the entire complexity space by the character pool size (71x), making search spaces grow exponentially.

Understanding Password Entropy

Password entropy measures the uncertainty of a password. A 16-character password generated using a CSPRNG and a 71-character pool yields 98 bits of entropy. This is considered secure against any modern threat. If you increase the length to 20 characters, the entropy reaches 123 bits, providing a massive safety margin that remains secure even against future quantum computer clusters.

Key Takeaway

Length is the king of password security because keyspace growth is exponential, making longer passwords mathematically immune to brute-force attacks.

With these engineering facts clear, we can now dismantle the most common security myths and summarize our findings.

Common Myths About Secure Password Generation

Myth 1: Math.random() is safe if I seed it myself. Seeding Math.random() does not change the fact that V8's xorshift128+ algorithm is linear. The state is still solvable, and an attacker can predict future outputs after observing a few values.

Myth 2: Adding symbols makes any password secure. A short password like p@ss1! is cracked instantly despite having symbols. Length and randomness are far more important than complexity rules.

Myth 3: An online password generator is always safe. Some generators send your password to their server or run on insecure code. A secure generator must run entirely on your local device with zero network traffic.

Myth 4: A password that is hard to remember is hard to crack. Human-created "complex" passwords (like P@ssw0rd123!) follow common substitution rules that password-cracking software automatically guesses first.

Myth 5: Modulo bias is a purely theoretical problem. In high-stakes cryptography, modulo bias is a real vulnerability. An attacker who knows that certain characters are 33% more likely to appear can optimize their search patterns to crack keys faster.

Myth 6: I should change my passwords every 90 days. Arbitrary rotation schedules lead users to choose predictable patterns (like Spring2026! followed by Summer2026!). Modern security bodies like NIST recommend only changing passwords if a breach is suspected.

Myth 7: A password manager makes me a single target. Password manager vaults are encrypted locally using your master key. Even if the manager's servers are breached, the vaults cannot be opened without your master key, which is far safer than reusing passwords.

Myth 8: A generator doesn't need to run locally. Running a generator on a remote server introduces the risk of network interception, server logging, and compromise. Local client-side generation is the only way to guarantee absolute privacy.

Key Takeaways Summary

  • Use CSPRNG: Never use Math.random() for security. Always verify that window.crypto.getRandomValues() is being called.
  • Eliminate Modulo Bias: Good generators use rejection sampling to map random bytes to characters uniformly.
  • Enforce Unbiased Policy: Use full-string rejection instead of template swapping to keep character selections independent.
  • Prioritize Length: Aim for at least 16 to 20 characters to ensure exponential keyspace resistance.
  • Run Locally: Generate passwords in your own browser tab, ensuring no data ever leaves your device.

Conclusion

Creating strong passwords is no longer a matter of human creativity. By understanding how cryptographically secure random number generators (CSPRNGs), system entropy, and rejection sampling work, we can replace unpredictable human habits with mathematically proven safety.

When you use a modern local generator powered by the Web Crypto API, you are not guessing a password; you are creating a cryptographic block that would take billions of years to break. In an era of automated attacks, that is the only level of safety that keeps your accounts secure.

Author's Note: This guide was written to demystify how digital security works at its most fundamental level. When we generate a password, we are not just choosing characters; we are constructing a cryptographic defense. By understanding the math and logic behind CSPRNGs, Web Crypto, and rejection sampling, we can make informed decisions to protect our digital lives. - Sreehari Pradeep
Related Tools & Reading:
🎲

Generate a Cryptographically Secure Password Instantly

Create exceptionally strong, unbiased random passwords on your device. Powered by the Web Crypto API, with real-time entropy calculation and zero data leaving your browser.

Open Secure Password Generator

Related Articles

June 18, 2026

How to Remove ChatGPT Formatting Before Copying to Word .

Copying text from ChatGPT into Microsoft Word should be simple, but it often isn't. Instead of clean paragraphs, you may end up with Markdown formatting, bold text, numbered lists, unwanted hyperlinks, extra blank lines, unusual punctuation, or inconsistent spacing.

July 2, 2026

How to Remove Gemini Links and References from Copied Text

If you've copied text from Google Gemini into Microsoft Word, Google Docs, Notion, or an email, you've probably noticed that it often includes hyperlinks, citations, source references, and formatting you don't actually want.

July 3, 2026

Remove AI Formatting Online (Without Losing Your Paragraphs)

Artificial intelligence tools like ChatGPT, Gemini, and Claude make writing fast, but copying their output often leaves behind messy Markdown, unwanted links, and formatting issues. Learn how to clean your text while preserving your paragraphs.