Skip to main content
Offensive360
Home / Knowledge Base / Insecure Randomness
Medium CWE-330 A02:2021 Cryptographic Failures

Insecure Randomness

Using weak or predictable random number generators for security-sensitive values like tokens, session IDs, and OTPs allows attackers to predict or brute-force these values. Learn which APIs to use.

Affects: C#JavaJavaScriptPHPPythonRubyGo

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 of Math.random() for generating tokens, IDs, nonces, or keys
  • java.util.Random for security values — Use of non-secure Random class in token/session generation paths
  • rand()/mt_rand() in PHP — PHP pseudo-random functions used where cryptographic randomness is required
  • random.random()/random.randint() in Python — Python’s random module used in authentication or token flows
  • Short token lengths — Token generation producing fewer than 128 bits of entropy

Remediation guidance

  1. Use platform CSPRNG APIscrypto.randomBytes() (Node.js), SecureRandom (Java), secrets module (Python 3.6+), random_bytes() (PHP 7+), crypto/rand (Go).

  2. Generate at least 128 bits (16 bytes) of entropy — For tokens, session IDs, and reset links. 256 bits is preferred for long-lived credentials.

  3. Never seed secure random with timestampsnew Random(System.currentTimeMillis()) or srand(time()) are as predictable as unseeded PRNGs.

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

  5. Set short expiry windows — Limit the attack surface by expiring tokens quickly (e.g., 15 minutes for password reset tokens).

References

By Offensive360 Security Research Reviewed: March 2026

Detect Insecure Randomness automatically

Run Offensive360 SAST on your codebase to find this and 100+ other vulnerabilities.