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).