How to Generate Random strings Securely in JavaScript
Learn how to generate random strings in JavaScript using crypto-secure methods. Covers UUIDs, random tokens, password generators, and common security pitfalls.
Random strings are everywhere in software — session tokens, password reset links, API keys, UUIDs. Getting them wrong creates security vulnerabilities that are hard to catch. This guide shows you how to do it properly.
Why It Matters
Computers are deterministic. Generating "random" strings with Math.random() or Date.now() isn't truly random — it's pseudorandom, predictable, and exploitable. If you use Math.random() to generate a password reset token, an attacker who can guess the seed can reset any user's password.
Rule: always use the Web Crypto API for security-sensitive randomness.
Generate a UUID v4
The simplest secure use case — a unique identifier with enough entropy that guessing is impossible.
// Modern browsers and Node.js 19+
const uuid = crypto.randomUUID();
console.log(uuid); // "f47ac10b-58cc-4372-a567-0e02e2d5f6b1"
// Node.js fallback (all versions)
import { randomUUID } from 'crypto';
const uuid = randomUUID();
Why this is secure: 122 bits of cryptographically strong entropy from the OS random number generator. Brute-forcing is computationally infeasible.
Generate a Random Token
For API keys, session tokens, or password reset links:
// Generate a 32-byte (256-bit) random token, base64 encoded
function generateToken(lengthBytes = 32) {
const bytes = crypto.getRandomValues(new Uint8Array(lengthBytes));
return btoa(String.fromCharCode(...bytes))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
const token = generateToken(); // "T8f9KwH3xLR2QpVl_YN7A..."
Key points:
crypto.getRandomValues()is the browser and Node.js API for secure randomness- Use
Uint8Arrayto get raw random bytes - Use base64url encoding (not plain base64) for URL-safe tokens
- Strip padding (
=) to keep it compact
Generate a Random Password
function generatePassword(length = 16) {
const charset = {
lowercase: 'abcdefghijklmnopqrstuvwxyz',
uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
numbers: '0123456789',
symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?',
};
const allChars = Object.values(charset).join('');
const entropy = new Uint8Array(length);
crypto.getRandomValues(entropy);
let password = '';
for (let i = 0; i < length; i++) {
password += allChars[entropy[i] % allChars.length];
}
// Ensure at least one char from each category
const required = [
charset.lowercase[entropy[0] % 26],
charset.uppercase[entropy[1] % 26],
charset.numbers[entropy[2] % 10],
charset.symbols[entropy[3] % charset.symbols.length],
];
// Replace first 4 chars with required, rest with random
return required.join('') + password.slice(4);
}
console.log(generatePassword()); // "Ab3@xK9LmNopQ2rs"
Note: This approach has a slight bias (modulo arithmetic isn't perfectly uniform), but for passwords it's negligible. For cryptographic keys, use a proper CSPRNG.
Generate a Cryptographic Key
For signing tokens, encrypting data, or any cryptographic purpose:
// Generate a 256-bit (32 byte) key for AES-256
const keyBytes = crypto.getRandomValues(new Uint8Array(32));
const keyHex = Array.from(keyBytes)
.map(b => b.toString(16).padStart(2, '0'))
.join('');
console.log(keyHex); // "a4f2c8d3e9b1..." (64 hex chars)
// For use with SubtleCrypto
crypto.subtle.importKey(
'raw',
keyBytes,
{ name: 'AES-GCM' },
false, // not extractable
['encrypt', 'decrypt']
);
Common Mistakes to Avoid
❌ Math.random() for Tokens
// NEVER do this
const token = Math.random().toString(36).substring(2);
// Predictable and low entropy
Math.random() is a PRNG (pseudo-random number generator) with only ~58 bits of state. It's fine for UI randomness (animations, shuffling), not security tokens.
❌ Date.now() or timestamp in Tokens
// NEVER do this
const token = btoa(Date.now() + Math.random());
// Entirely predictable — attacker knows approximately when token was created
❌ Short Tokens
A 6-character alphanumeric token has only ~77 bits of entropy (log2(62^6)). That's breakable with commodity hardware in hours. Use at least 128 bits (22 base64 characters) for security-sensitive tokens.
❌ Sequential IDs as Secrets
Auto-increment IDs, UUID v1 (time-based), or any enumerable ID are not secrets. They look random but aren't.
Quick Reference
| Purpose | Method | Bits of Entropy |
|---|---|---|
| UUID | crypto.randomUUID() |
122 bits |
| API token | crypto.getRandomValues() + base64url |
256 bits |
| Password | crypto.getRandomValues() with charset |
~100 bits |
| Cryptographic key | crypto.getRandomValues() |
256 bits |
| Nonce/IV | crypto.getRandomValues() |
96–256 bits |
In Node.js
import { randomBytes, randomUUID } from 'crypto';
// Random bytes
const bytes = randomBytes(32); // Buffer of 32 random bytes
// UUID
const uuid = randomUUID();
// Hash-based (for deterministic tokens with a secret)
import { createHmac } from 'crypto';
const token = createHmac('sha256', secret)
.update(randomBytes(32).toString('hex'))
.digest('hex');
Useful Tools
- UUID Generator — Generate UUIDs v4 instantly in your browser
- Hash Generator — Generate SHA/MD5 hashes
- Base64 Encoder — Encode any string to base64
No data leaves your browser. Everything runs client-side using the Web Crypto API.
Toolblip Team
Writing about developer tools, web performance, and the tools that make building faster.

