What Are Cryptographically Secure Numbers and How to Generate Them in JavaScript

What Are Cryptographically Secure Numbers and How to Generate Them in JavaScript

Ever wondered why your password manager generates those impossibly random passwords, or how secure tokens are created for authentication?

The secret sauce is cryptographically secure random numbers and they're more important (and interesting) than you might think.

In this article, I will discuss just what these numbers are, how to generate them and why they matter.

The Problem with Regular Random Numbers

JavaScript's built-in Math.random() function seems like it should be perfect for generating random numbers, right? Well, not quite. While it's great for games, animations, or picking a random item from an array, it has a fatal flaw when it comes to security: it's predictable.

// This is NOT cryptographically secure
const notSecure = Math.random();
console.log(notSecure); // 0.8394720958480349

The issue is that Math.random() uses what's called a pseudorandom number generator (PRNG). These algorithms are designed to be fast and produce numbers that look random, but if you know the internal state or seed, you can predict all future numbers.

That's a big problem when you're generating passwords, session tokens, or encryption keys.

What Makes a Number Cryptographically Secure?

Cryptographically secure random numbers have three key properties:

Unpredictability: Even if you know a million previous numbers, you can't predict the next one. The sequence should be statistically indistinguishable from true randomness.

Non-reproducibility: Unlike pseudorandom generators that use seeds, cryptographically secure generators shouldn't produce the same sequence twice, even if started under identical conditions.

Entropy: The numbers should have high entropy, meaning each bit has roughly a 50% chance of being 0 or 1, with no patterns or biases.

Think of it like the difference between a coin flip and a rigged coin. A fair coin gives you true randomness, each flip is independent and unpredictable. A rigged coin might look random at first glance, but once you figure out the pattern, you can predict future flips.

Enter the Web Crypto API

Fortunately, modern JavaScript environments provide the Web Crypto API, which includes crypto.getRandomValues(). This function taps into your operating system's cryptographically secure random number generator.

// Generate cryptographically secure random bytes
const array = new Uint8Array(10);
crypto.getRandomValues(array);
console.log(array); // Uint8Array(10) [142, 74, 26, 89, 201, 45, 193, 88, 12, 176]

The beauty of crypto.getRandomValues() is that it's backed by your OS's entropy sources, things like mouse movements, keyboard timings, disk activity, and specialized hardware random number generators.

Practical Examples

Generating Random Integers

Want a cryptographically secure random integer within a specific range? Here's how:

function getSecureRandomInt(min, max) {
  const range = max - min + 1;
  const array = new Uint32Array(1);
  
  crypto.getRandomValues(array);
  return min + (array[0] % range);
}

// Generate a secure random number between 1 and 100
const secureNumber = getSecureRandomInt(1, 100);
console.log(secureNumber); // 47 (or any number 1-100)

Creating Random IDs

Perfect for generating session tokens or unique identifiers:

function generateSecureId(length = 16) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const array = new Uint8Array(length);
  
  crypto.getRandomValues(array);
  
  return Array.from(array, byte => chars[byte % chars.length]).join('');
}

const sessionToken = generateSecureId(32);
console.log(sessionToken); // "kF8mN2pQ7vX9zA1bC4dE6gH8jK0mN2pQ"

Random Passwords

Here's a simple secure password generator:

function generatePassword(length = 12) {
  const lowercase = 'abcdefghijklmnopqrstuvwxyz';
  const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  const numbers = '0123456789';
  const symbols = '!@#$%^&*()_+-=[]{}|;:,.<>?';
  
  const allChars = lowercase + uppercase + numbers + symbols;
  const array = new Uint8Array(length);
  
  crypto.getRandomValues(array);
  
  return Array.from(array, byte => allChars[byte % allChars.length]).join('');
}

const password = generatePassword(16);
console.log(password); // "K9#mP2$vN8&qR5@z"

Browser and Node.js Support

The Web Crypto API is well-supported in modern browsers, but what about Node.js? Good news, Node.js has its own crypto module with similar functionality:

// In Node.js
const crypto = require('crypto');

// Generate random bytes
const randomBytes = crypto.randomBytes(16);
console.log(randomBytes.toString('hex'));

// For a more direct equivalent to crypto.getRandomValues()
const array = new Uint8Array(10);
crypto.getRandomValues(array); // Available in Node.js 15+

When to Use Cryptographically Secure Random Numbers

You should reach for cryptographically secure random numbers whenever security matters:

  • Generating passwords or passphrases
  • Creating session tokens or API keys
  • Building authentication challenges
  • Generating encryption keys or initialization vectors
  • Creating secure random identifiers
  • Implementing cryptographic protocols

For everything else, game mechanics, animations, random colors for your UI, Math.random() is perfectly fine and much faster.

A Word of Caution

While crypto.getRandomValues() gives you cryptographically secure random bytes, remember that security is only as strong as its weakest link. If you're building something mission-critical, consider:

  • How you store and transmit these random values
  • Whether your random number generation is happening client-side (where users have control)
  • The overall security architecture of your application

Wrapping Up

Cryptographically secure random numbers might seem like overkill for simple web apps, but they're essential whenever you're dealing with user security. The Web Crypto API makes it easy to generate truly unpredictable numbers that would make even the most determined attacker throw in the towel.

Next time you need to generate a token, password, or any random value where security matters, skip Math.random() and reach for crypto.getRandomValues(). Your users' security will thank you for it.

Walt is a computer scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.

Community Comments

No comments posted yet

Code Your Own Classic Snake Game – The Right Way

Master the fundamentals of game development and JavaScript with a step-by-step guide that skips the fluff and gets straight to the real code.

"Heavy scripts slowing down your site? I use Fathom Analytics because it’s lightweight, fast, and doesn’t invade my users privacy."
Ad Unit

Current Poll

Help us and the community figure out what the latest trends in coding are.

Total Votes:
Q:
Submit

Add a comment