›The Shack ›Developer Tips ›Secure Random Numbers in JavaScript

How to Generate Random Numbers Securely in JavaScript (and Online)

Back to All Posts

Random numbers come up constantly in development: shuffling arrays, picking a sample from a dataset, generating test data, assigning IDs, running simulations, building games. Most of the time Math.random() works fine. But for anything touching security — tokens, passwords, cryptographic keys, CSRF values — it absolutely does not, and reaching for it out of habit is a meaningful vulnerability.

Understanding the difference between pseudorandom and cryptographically secure random numbers, and knowing which API to reach for, keeps that mistake from happening. The Random Number Generator on DevToolShack produces cryptographically secure random numbers in your browser for cases where you just need a value quickly without writing code.

Pseudorandom vs Cryptographically Secure

A pseudorandom number generator (PRNG) produces a sequence of numbers that looks random but is entirely deterministic. Given the same starting seed, it produces the same sequence every time. The numbers are statistically well-distributed and pass randomness tests, but they are not unpredictable — an attacker who knows or can guess the seed can reproduce the entire output.

Math.random() in JavaScript is a PRNG. The V8 engine uses the xorshift128+ algorithm, which is fast and produces good statistical distributions. It is perfectly appropriate for simulations, games, shuffles, and any non-security use. It is not appropriate for generating tokens, session IDs, passwords, or cryptographic material, because its output could in principle be predicted.

A cryptographically secure pseudorandom number generator (CSPRNG) draws its entropy from the operating system's entropy pool, which collects genuine randomness from hardware events (timing of keystrokes, disk I/O, network packets, thermal noise). This makes its output computationally infeasible to predict even if you know the algorithm being used.

The Secure API: crypto.getRandomValues()

In browsers and Node.js, the correct API for cryptographically secure random numbers is crypto.getRandomValues(). It fills a typed array with random bytes from the OS entropy pool.

// Fill a Uint32Array with 4 random 32-bit integers
const array = new Uint32Array(4);
crypto.getRandomValues(array);
console.log(array); // [3847291022, 912847302, 2938471029, 748291847]

// Generate a random integer in range [min, max] (inclusive)
function secureRandInt(min, max) {
  const range = max - min + 1;
  const bytesNeeded = Math.ceil(Math.log2(range) / 8);
  const maxValid = Math.floor(256 ** bytesNeeded / range) * range - 1;
  let value;
  do {
    const bytes = new Uint8Array(bytesNeeded);
    crypto.getRandomValues(bytes);
    value = bytes.reduce((acc, b) => acc * 256 + b, 0);
  } while (value > maxValid); // rejection sampling to avoid modulo bias
  return min + (value % range);
}

The rejection sampling loop in that example avoids modulo bias — a subtle error where values at the low end of a range are slightly more likely than values at the high end when the range doesn't divide evenly into the maximum possible output. For most applications the bias is negligible, but for security-sensitive use cases it matters.

In Node.js, crypto.getRandomValues() is available globally since Node 19. For older versions, use the built-in crypto module:

const { randomInt, randomBytes } = require('crypto');

// Secure random integer in [min, max)
const n = randomInt(1, 101); // 1 to 100 inclusive

// 32 random bytes as a hex string (for tokens, keys, etc.)
const token = randomBytes(32).toString('hex');

When Math.random() Is Fine

The security distinction matters, but it is easy to over-apply it. Math.random() is appropriate for:

  • Shuffling a playlist or card deck
  • Picking a random item from an array for display purposes
  • Generating test data with random-looking values
  • Simulations and probabilistic algorithms
  • Games where reproducibility (via a seed) might even be desirable
  • Any UI randomness where an attacker gaining knowledge of future values provides no meaningful advantage

Use crypto.getRandomValues() whenever the random value will be used for authentication, authorization, or any context where unpredictability is a security requirement: session tokens, password reset links, CSRF tokens, API keys, cryptographic nonces, and similar values. The post on random string generators covers the same distinction applied to generating random strings rather than integers.

Generating a Random Number in a Range

The standard Math.random() pattern for a range is:

// [min, max) — includes min, excludes max
Math.floor(Math.random() * (max - min)) + min;

// [min, max] — includes both
Math.floor(Math.random() * (max - min + 1)) + min;

For a secure equivalent without rolling your own implementation, Node's crypto.randomInt(min, max) handles range generation correctly including bias prevention. In browsers, the secureRandInt function above does the same thing.

Seeded Random Number Generators

Neither Math.random() nor crypto.getRandomValues() lets you set a seed. Sometimes reproducibility is exactly what you need — for procedural generation, reproducible test suites, or simulations that need to be replayed. For seeded random number generation in JavaScript, libraries like seedrandom provide deterministic PRNGs you can initialize with a known value. These are never suitable for security use but are useful for reproducible randomness.

UUID v4 uses crypto.getRandomValues(). When you generate a UUID v4, you're generating 122 bits of cryptographically random data formatted as a UUID string. That's why UUID v4 values are safe to use as unpredictable identifiers. The UUID Generator on DevToolShack generates v4 UUIDs using the Web Crypto API.

Using the Random Number Generator

The Random Number Generator on DevToolShack uses crypto.getRandomValues() under the hood, so every number it produces is cryptographically secure. You can set a minimum, maximum, and quantity, making it useful for generating test data, lottery draws, sampling, or any situation where you need random integers quickly without opening a terminal.

For generating random strings rather than integers — API keys, tokens, invite codes — the Random String Generator offers configurable character sets and lengths, also using the Web Crypto API. And for generating secure passwords, the Password Generator applies the same cryptographic randomness to a password-appropriate character set.