What is Insecure Randomness?
Insecure randomness occurs when an application uses a predictable or non-cryptographic random number generator (PRNG) to produce values that must be unpredictable for security — such as session tokens, password reset links, CSRF tokens, encryption keys, or one-time passwords.
Standard PRNGs like Math.random() (JavaScript), java.util.Random, random.random() (Python), and rand() (PHP/C) are designed for speed and statistical distribution, not for security. Their internal state can often be inferred from a small number of observed outputs, allowing an attacker to predict past or future values.
How exploitation works
A password reset token is generated with Math.random():
// 16 hex chars from Math.random() — ~53 bits, observable seed
const token = Math.random().toString(16).substr(2);
An attacker requests multiple password resets, observes the tokens, and uses them to reconstruct the V8 engine’s PRNG state — then predicts future tokens for other accounts. Tools like untwister automate Mersenne Twister state recovery from observed Math.random() outputs.
Vulnerable code examples
JavaScript / Node.js
// VULNERABLE: Math.random() is not cryptographically secure
function generateResetToken() {
return Math.random().toString(36).substr(2, 16); // Predictable
}
Java
// VULNERABLE: java.util.Random is seeded from system time — predictable
import java.util.Random;
public String generateSessionId() {
Random rng = new Random(); // Or new Random(System.currentTimeMillis())
return Long.toHexString(rng.nextLong());
}
PHP
// VULNERABLE: rand() and mt_rand() are not cryptographically secure
$token = bin2hex(pack('N', mt_rand()));
Secure code examples
JavaScript / Node.js — crypto.randomBytes
// SECURE: Node.js crypto module uses OS entropy source (CSPRNG)
const crypto = require('crypto');
function generateResetToken() {
return crypto.randomBytes(32).toString('hex'); // 256-bit secure token
}
Java — SecureRandom
// SECURE: java.security.SecureRandom uses OS entropy (e.g., /dev/urandom)
import java.security.SecureRandom;
import java.util.Base64;
public String generateSessionId() {
SecureRandom secureRng = new SecureRandom();
byte[] bytes = new byte[32];
secureRng.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
Python — secrets module
# SECURE: secrets module is designed for security-sensitive values (Python 3.6+)
import secrets
def generate_otp():
return secrets.token_hex(16) # 128 bits of randomness
def generate_numeric_otp(digits=6):
return str(secrets.randbelow(10**digits)).zfill(digits)
PHP — random_bytes
// SECURE: random_bytes() uses CSPRNG
$token = bin2hex(random_bytes(32)); // 256-bit token
What Offensive360 detects
Math.random()in security contexts — Usage ofMath.random()for generating tokens, IDs, nonces, or keysjava.util.Randomfor security values — Use of non-secureRandomclass in token/session generation pathsrand()/mt_rand()in PHP — PHP pseudo-random functions used where cryptographic randomness is requiredrandom.random()/random.randint()in Python — Python’srandommodule used in authentication or token flows- Short token lengths — Token generation producing fewer than 128 bits of entropy
Remediation guidance
-
Use platform CSPRNG APIs —
crypto.randomBytes()(Node.js),SecureRandom(Java),secretsmodule (Python 3.6+),random_bytes()(PHP 7+),crypto/rand(Go). -
Generate at least 128 bits (16 bytes) of entropy — For tokens, session IDs, and reset links. 256 bits is preferred for long-lived credentials.
-
Never seed secure random with timestamps —
new Random(System.currentTimeMillis())orsrand(time())are as predictable as unseeded PRNGs. -
Store token hashes, not plaintext — Even with secure generation, store SHA-256 hashes of reset tokens in the database to prevent exposure in case of a database breach.
-
Set short expiry windows — Limit the attack surface by expiring tokens quickly (e.g., 15 minutes for password reset tokens).