Weakness reference
CWE-338

Use of Cryptographically Weak PRNG

This weakness occurs when software uses a non-cryptographic random number generator like random or mtrand to create security-sensitive values such as session…

01Summary

This weakness occurs when software uses a non-cryptographic random number generator (like random() or mt_rand()) to create security-sensitive values such as session tokens, password reset links, or encryption keys. Because these generators are designed for simulation and games—not security—an attacker can predict or reproduce the values they produce, bypassing authentication or encryption protections.

02How It Happens

Non-cryptographic PRNGs are fast and deterministic by design: they produce sequences that *appear* random but follow a mathematical pattern. If an attacker knows or can guess the seed (initial state), they can reproduce the entire sequence. Even without the seed, weak generators often have short internal state spaces or predictable patterns that allow an attacker to narrow down possibilities or reverse-engineer past outputs. When these generators are used for security tokens, session IDs, or cryptographic material, an attacker can forge valid tokens, hijack sessions, or break encryption—defeating the entire security mechanism that depends on unpredictability.

03Real-World Impact

An attacker exploiting this weakness can forge session tokens and impersonate legitimate users, reset passwords by predicting the reset token, or generate valid API keys without authorization. In some cases, weak randomness in cryptographic key generation can allow an attacker to recover the key itself, rendering encryption useless. The impact ranges from account takeover to complete compromise of confidentiality and integrity.

04Vulnerable & Fixed Patterns

Vulnerable pattern
import random

def generate_session_token():
    # Weak: random module is not cryptographically secure
    token = ''.join(random.choice('abcdef0123456789') for _ in range(32))
    return token

def generate_password_reset_link(user_id):
    # Weak: predictable token based on weak PRNG
    reset_code = random.randint(100000, 999999)
    return f"https://example.com/reset?user={user_id}&code={reset_code}"

Why it's vulnerable:
The random module uses a Mersenne Twister algorithm, which is fast but not cryptographically secure. An attacker who observes a few outputs can predict future tokens or brute-force the seed.

Fixed pattern
import secrets

def generate_session_token():
    # Secure: secrets module uses OS entropy
    token = secrets.token_hex(16)  # 32 hex characters
    return token

def generate_password_reset_link(user_id):
    # Secure: cryptographically strong random value
    reset_code = secrets.randbelow(1000000)
    return f"https://example.com/reset?user={user_id}&code={reset_code}"
Vulnerable pattern
<?php
function generate_session_token() {
    // Weak: mt_rand is not cryptographically secure
    $token = '';
    for ($i = 0; $i < 32; $i++) {
        $token .= dechex(mt_rand(0, 15));
    }
    return $token;
}

function generate_password_reset_link($user_id) {
    // Weak: predictable token
    $reset_code = mt_rand(100000, 999999);
    return "https://example.com/reset?user=$user_id&code=$reset_code";
}
?>

Why it's vulnerable:
mt_rand() is faster than rand() but still not suitable for security. Its internal state can be recovered from observed outputs, allowing an attacker to predict future tokens.

Fixed pattern
<?php
function generate_session_token() {
    // Secure: random_bytes uses OS entropy
    $token = bin2hex(random_bytes(16));
    return $token;
}

function generate_password_reset_link($user_id) {
    // Secure: cryptographically strong random value
    $reset_code = random_int(100000, 999999);
    return "https://example.com/reset?user=$user_id&code=$reset_code";
}
?>

05Prevention Checklist

Use only cryptographically secure random generators: secrets module (Python 3.6+), random.SystemRandom() (Python), random_bytes() or random_int() (PHP 7+), or equivalent OS-provided entropy in other languages.
Never use random(), mt_rand(), Math.random(), or similar general-purpose PRNGs for security-sensitive values.
Audit all code that generates session tokens, password reset codes, API keys, CSRF tokens, or encryption keys; replace weak generators immediately.
If you must support legacy PHP versions without random_bytes(), use openssl_random_pseudo_bytes() as a fallback.
Document which random generator is used for each security-sensitive operation and why it was chosen.

06Signs You May Already Be Affected

Look for use of random(), mt_rand(), Math.random(), or similar non-cryptographic functions in code that handles authentication tokens, session IDs, or password reset links. Check server logs for unusual patterns of token reuse or rapid token generation from a single source. If you observe attackers successfully forging tokens or resetting passwords without legitimate access, weak randomness may be the root cause.

07Related Recent Vulnerabilities