Generating random values sounds simple, until you need randomness that is actually secure.
A lot of JavaScript developers reach for Math.random() out of habit. While that works fine for visual effects, games, or non-critical IDs, it should never be used for anything security-sensitive.
That’s where the Web Crypto API comes in.
The problem with Math.random()
Math.random() is not cryptographically secure.
Its output is deterministic and predictable enough that an attacker may be able to reproduce or guess generated values under certain conditions.
That makes it unsuitable for:
- Session tokens
- Password reset links
- API keys
- CSRF tokens
- Encryption keys
- Secure identifiers
Example:
const id = Math.random().toString(36).slice(2)
This may look random, but it is not secure.
Using crypto.getRandomValues()
Modern browsers provide a secure random number generator through the Web Crypto API.
Example:
const bytes = new Uint8Array(16)
crypto.getRandomValues(bytes)
console.log(bytes)
This fills the typed array with cryptographically secure random bytes provided by the operating system.
Generating a secure random token
A common use case is generating secure tokens or identifiers.
function generateToken(length = 32) {
const bytes = new Uint8Array(length)
crypto.getRandomValues(bytes)
return Array.from(bytes)
.map(byte => byte.toString(16).padStart(2, "0"))
.join("")
}
console.log(generateToken())
Example output:
4f8b7d3a1f9e0c8d7a2b6c5d4e3f1a9c
This is significantly safer than using Math.random().
Typed arrays are required
crypto.getRandomValues() only works with integer-based typed arrays.
Supported examples include:
new Uint8Array(32)
new Uint16Array(16)
new Int32Array(8)
This will fail:
crypto.getRandomValues([])
Because regular JavaScript arrays are not supported.
Browser and runtime support
crypto.getRandomValues() is widely supported in modern browsers.
It is also available in runtimes like:
- Node.js
- Deno
- Bun
Example in Node.js:
const bytes = new Uint8Array(16)
crypto.getRandomValues(bytes)
console.log(bytes)
In older Node.js versions, developers typically used:
require("crypto").randomBytes(16)
UUID generation
If your goal is generating UUIDs, modern runtimes also support:
crypto.randomUUID()
Example:
550e8400-e29b-41d4-a716-446655440000
Internally, this also uses cryptographically secure randomness.
Things to remember
- Use
Math.random()for non-security-related randomness only - Use
crypto.getRandomValues()for anything security-sensitive - Prefer
crypto.randomUUID()when generating UUIDs - Always generate randomness using the operating system’s secure RNG
For modern JavaScript applications, crypto.getRandomValues() should be the default choice whenever security matters.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.