SecureBlockLog inStart a pentest
Vulnerability Repository
MediumCryptography

Insecure Randomness

Use of predictable pseudo-random number generators for security-sensitive operations allows attackers to predict tokens, session IDs, or cryptographic keys.

CVSS 5.9CWE CWE-330OWASP A02:2021 — Cryptographic Failures

Description

Insecure randomness (CWE-330 — Use of Insufficiently Random Values) occurs when an application uses a pseudo-random number generator (PRNG) that is predictable or seeded with a low-entropy value for security-critical operations such as generating session tokens, password reset links, CSRF tokens, API keys, cryptographic nonces, or one-time passwords. A PRNG that is seeded with the current timestamp, a process ID, or any other guessable value will produce an output that an attacker can predict or reconstruct given a small number of observed values.

This is classified under CWE-330 and A02:2021 — Cryptographic Failures because the failure is fundamentally cryptographic: the generated values lack the statistical unpredictability required for their security function. The distinction between a PRNG and a Cryptographically Secure PRNG (CSPRNG) is critical: a standard PRNG (like Python's random, Java's java.util.Random, JavaScript's Math.random, PHP's rand()) is deterministic given its seed and is not suitable for security use. A CSPRNG draws entropy from the operating system's entropy pool and is non-deterministic from an attacker's perspective.

The impact varies by the security function affected. Predictable session IDs enable session hijacking. Predictable password reset tokens enable account takeover. Predictable cryptographic nonces enable message forgery or key recovery.

How It Works

Predictable session token generation using a timestamp-seeded PRNG:

# Vulnerable: Python random seeded with time
import random
import time

random.seed(int(time.time()))
session_token = hex(random.getrandbits(64))

If the attacker knows approximately when a session was created (e.g., they see a login timestamp in the response), they can brute-force the seed across a 1-2 second window, generating all possible tokens and trying each one.

Java's java.util.Random is a linear congruential generator. After observing just two consecutive outputs, an attacker can mathematically reconstruct the entire internal state and predict all future outputs:

// Vulnerable
Random random = new Random();  // Seeded from System.nanoTime()
String token = Long.toHexString(random.nextLong());

// Safe
SecureRandom secureRandom = new SecureRandom();
String token = Long.toHexString(secureRandom.nextLong());

JavaScript Math.random() is explicitly documented as not cryptographically secure. Yet it appears in numerous token generation snippets copied from Stack Overflow:

// Vulnerable — seen in production codebases
function generateToken() {
  return Math.random().toString(36).substring(2);
}

// Safe
const { randomBytes } = require('crypto');
function generateToken() {
  return randomBytes(32).toString('hex');
}

Low-entropy mobile app tokens are common on Android, where java.util.Random is frequently used in place of java.security.SecureRandom. The Google Android team has documented vulnerabilities caused by developers using the wrong PRNG class.

UUID v1 is not random. UUID version 1 encodes the MAC address and timestamp. If an attacker knows or can guess the server's MAC address and approximate time, they can predict UUID v1 values generated in a time window.

Impact

  • Session hijacking — predictable session IDs allow attackers to enumerate and guess active sessions.
  • Account takeover — predictable password reset tokens allow requesting a reset and guessing the token before the victim uses it.
  • CSRF token bypass — predictable CSRF tokens allow forging cross-site requests.
  • Cryptographic key predictability — weak entropy in key generation produces keys that are vulnerable to brute-force attacks much smaller than the theoretical key space.
  • OTP/2FA bypass — predictable one-time password seeds allow computing future codes without the user's device.
  • API key compromise — predictable API keys can be brute-forced in a manageable search space.

Detection

  1. Review all token generation code — search the codebase for Math.random(), random.random(), java.util.Random, rand() (PHP), and Random() in contexts that generate session IDs, tokens, or keys.
  2. Collect and analyze tokens statistically — request a large number of password reset tokens or session IDs and analyze them with Burp Sequencer (analyze token entropy) or custom scripts. Low-entropy values will show statistical patterns.
  3. Test timestamp-based seeds — for suspected timestamp-seeded PRNGs, generate tokens around a known time, reconstruct the seed, and attempt to predict the target token.
  4. Check UUID versions — collect generated UUIDs and determine the version. Version 1 UUIDs require additional investigation of the MAC/timestamp encoding.
  5. Inspect mobile app code — use jadx (Android) or class-dump (iOS) to find PRNG usages. Flag any use of java.util.Random or equivalent for security contexts.
  6. Check JavaScript dependencies — some UUID libraries and token generators use Math.random() as a fallback. Check the library source or use a package scanner.

Remediation

Always use a CSPRNG for security-sensitive values. Use the language's cryptographically secure generator:

# Python
import secrets
token = secrets.token_hex(32)   # 256-bit token
token = secrets.token_urlsafe(32)

# Node.js
const { randomBytes } = require('crypto');
const token = randomBytes(32).toString('hex');

# Java
import java.security.SecureRandom;
SecureRandom sr = new SecureRandom();
byte[] bytes = new byte[32];
sr.nextBytes(bytes);

# PHP
$token = bin2hex(random_bytes(32));  // PHP 7+

Generate tokens with sufficient length. A minimum of 128 bits (16 bytes, 32 hex characters) of cryptographically random data is appropriate for session tokens and password reset links. 256 bits (32 bytes) is preferable.

Use your framework's built-in session management. Mature frameworks (Express, Django, Laravel, Rails) use CSPRNGs for session ID generation by default. Use the framework's session management rather than implementing your own.

Replace UUID v1 with UUID v4 in all security contexts. Verify that the UUID v4 implementation uses a CSPRNG as its entropy source.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping