Weakness reference
CWE-340

Generation of Predictable Numbers or Identifiers

This weakness occurs when software generates security-sensitive values—such as session tokens, password-reset links, or API keys—using predictable methods like…

01Summary

This weakness occurs when software generates security-sensitive values—such as session tokens, password-reset links, or API keys—using predictable methods like sequential counters, timestamps, or weak random sources. An attacker who understands the generation scheme can guess or forge valid tokens, bypassing authentication or authorization controls.

02How It Happens

Predictable identifier generation typically stems from one of three patterns: using a simple counter or incrementing ID, deriving values from easily observable data like timestamps or user IDs, or relying on a weak random number generator that produces a limited set of outcomes or follows a detectable pattern. Developers often choose these methods for convenience—they're simple to implement and debug—without realizing that security-sensitive tokens must be cryptographically unpredictable. An attacker who can observe a few valid tokens or understand the underlying algorithm can often predict future ones, gaining unauthorized access to accounts or sessions.

03Real-World Impact

Predictable tokens enable account takeover, session hijacking, and privilege escalation. For example, if password-reset tokens are based on a timestamp, an attacker can generate a list of plausible tokens and attempt them against a target account. If session IDs increment sequentially, an attacker who observes one session can predict others and impersonate different users. In multi-tenant systems, predictable API keys or resource identifiers can allow one user to access another's data. The impact scales with the sensitivity of what the token protects.

04Vulnerable & Fixed Patterns

Vulnerable pattern
import time
import hashlib

def generate_session_token(user_id):
    # Vulnerable: token derived from timestamp and user ID
    timestamp = int(time.time())
    token_input = f"{user_id}:{timestamp}"
    token = hashlib.md5(token_input.encode()).hexdigest()
    return token

def generate_reset_token(email):
    # Vulnerable: counter-based token
    counter = 12345
    token = f"reset_{counter}_{email}"
    return token

Why it's vulnerable:
The first example uses a timestamp (observable and limited in range) combined with a known user ID; an attacker can brute-force all tokens for a given minute. The second uses a simple counter that increments predictably, making it trivial to guess valid tokens.

Fixed pattern
import secrets

def generate_session_token(user_id):
    # Fixed: cryptographically random token
    token = secrets.token_urlsafe(32)
    return token

def generate_reset_token(email):
    # Fixed: cryptographically random token
    token = secrets.token_urlsafe(32)
    return token
Vulnerable pattern
<?php
function generate_session_token($user_id) {
    // Vulnerable: token based on timestamp and user ID
    $timestamp = time();
    $token = md5($user_id . ':' . $timestamp);
    return $token;
}

function generate_reset_token($email) {
    // Vulnerable: counter-based token
    static $counter = 1000;
    $counter++;
    return 'reset_' . $counter . '_' . $email;
}
?>

Why it's vulnerable:
The first example combines a predictable timestamp with a known user ID, allowing an attacker to generate candidate tokens for a narrow time window. The second uses a static counter that increments sequentially, making valid tokens easily guessable.

Fixed pattern
<?php
function generate_session_token($user_id) {
    // Fixed: cryptographically random token
    $token = bin2hex(random_bytes(32));
    return $token;
}

function generate_reset_token($email) {
    // Fixed: cryptographically random token
    $token = bin2hex(random_bytes(32));
    return $token;
}
?>

05Prevention Checklist

Use cryptographically secure random number generators (secrets in Python, random_bytes() in PHP) for all security-sensitive tokens.
Generate tokens with sufficient length and entropy (at least 128 bits for most use cases; 256 bits for high-value tokens like password resets).
Never derive tokens from user-observable or easily guessable data (timestamps, user IDs, sequential counters).
Store tokens securely (hashed in a database, never in plain text) and associate them with an expiration time.
Validate tokens server-side on every use; do not trust client-supplied token values.
Rotate or invalidate tokens after use (e.g., one-time password-reset links) or after a reasonable session duration.

06Signs You May Already Be Affected

Review your authentication and session logs for patterns: if you notice that session IDs or reset tokens follow a sequential or time-based pattern, or if you can predict valid tokens by observing a few examples, the generation scheme is likely predictable. Check for unauthorized account access or session hijacking incidents that correlate with token reuse or guessing attempts. Audit your codebase for use of time(), simple counters, or weak random functions in token generation.

07Related Recent Vulnerabilities