Skip to content

CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)

Overview

Weak PRNG occurs when applications use cryptographically insecure random number generators for security-sensitive operations, making it possible for attackers to predict or reproduce random values.

OWASP Classification

A04:2025 - Cryptographic Failures

Risk

High: Attackers can predict session tokens, keys, or other sensitive values, leading to account compromise, privilege escalation, or data breaches.

Remediation Steps

Core principle: Use a cryptographically secure PRNG for any security-sensitive randomness; never roll your own RNG.

Locate the weak PRNG usage in your code

  • Review the flaw details to identify the specific file, line number, and code pattern
  • Identify which weak PRNG is in use (e.g., random, Math.random, java.util.Random)
  • Trace the data flow to understand what the random value is used for (session tokens, cryptographic keys, nonces, salts)
  • Check for predictable seeding patterns (time-based seeds, PID-based seeds, static seeds)

Use cryptographically secure PRNGs (Primary Defense)

  • Replace weak PRNGs with secure alternatives:
    • Python: Use secrets module or os.urandom() instead of random
    • Java: Use java.security.SecureRandom instead of java.util.Random
    • JavaScript/Node.js: Use crypto.randomBytes() or crypto.getRandomValues() instead of Math.random()
    • .NET: Use System.Security.Cryptography.RandomNumberGenerator instead of System.Random
    • PHP: Use random_bytes() or random_int() instead of rand() or mt_rand()
  • Avoid standard PRNGs for security purposes: random, Math.random(), java.util.Random are NOT cryptographically secure

Avoid predictable seeding and validate randomness

  • Let OS provide entropy: Do not use time, PID, or other predictable values as seeds
  • No manual seeding: Cryptographically secure PRNGs (SecureRandom, secrets) handle seeding automatically from OS entropy pool
  • Validate randomness strength: Ensure random values are sufficiently long (128+ bits for tokens, 256+ bits for keys) and unpredictable
  • Use appropriate randomness for each use case: Session tokens, cryptographic keys, password reset tokens, salts all have different length and strength requirements

Apply additional PRNG protections

  • Generate fresh random values: Don't reuse random values across sessions, users, or requests
  • Avoid combining weak and strong sources: Don't mix random with secrets - use only secure PRNGs
  • Encode random values properly: Use base64url or hex encoding for tokens to prevent character encoding issues
  • Test randomness quality: Use statistical tests (chi-square, runs test) to verify output distribution

Monitor and audit random number generation

  • Review code for insecure random number generation (grep for random., Math.random, rand(, new Random())
  • Log random value generation for security-critical operations (session creation, API key generation, token creation)
  • Alert on failures or suspicious patterns (repeated values, sequential patterns, low entropy warnings)
  • Track PRNG performance and entropy pool status

Test the PRNG fix thoroughly

  • Verify the specific weak PRNG is no longer used for security purposes
  • Test with multiple generated values to ensure uniqueness and unpredictability
  • Verify session tokens, keys, salts use proper length and secure PRNG
  • Statistical testing: chi-square test, Kolmogorov-Smirnov test to verify randomness distribution
  • Re-scan with security scanner to confirm the issue is resolved

Language-Specific Guidance

For detailed examples and best practices in specific languages:

  • Python - Using secrets module, os.urandom(), Flask/Django/FastAPI examples
  • Java - Using SecureRandom, Spring Boot/JAX-RS examples
  • JavaScript/Node.js - Using crypto.randomBytes(), Express/Fastify/Next.js examples
  • PHP - Using random_bytes(), random_int(), Laravel/Symfony/WordPress examples

Common Vulnerable Patterns

  • Using random or Math.random for cryptographic purposes
  • Seeding PRNGs with predictable values

Predictable Random Number Generator (Python)

# Insecure: predictable random value
import random
key = random.getrandbits(128)  # NOT cryptographically secure
token = str(random.randint(0, 999999))  # Predictable session token

Why this is vulnerable: Python's random module uses the Mersenne Twister algorithm (MT19937) which is deterministic and can be completely reversed by observing 624 consecutive 32-bit outputs. If an attacker collects enough random values generated by the application (from session tokens, API responses, or timing observations), they can reconstruct the internal PRNG state and predict all future random values. A 6-digit token (0-999999) has only ~20 bits of entropy, allowing brute-force attacks in milliseconds. Even random.getrandbits(128) appears to provide 128 bits but uses a predictable algorithm - if the seed is known or guessable (often time-based), the entire sequence is reproducible. For session tokens, this means an attacker can forge valid sessions; for cryptographic keys, the encryption is effectively broken.

Secure Patterns

Cryptographically Secure Random Generator (Python)

# SECURE - cryptographically strong random value
import secrets
key = secrets.token_bytes(16)  # 128-bit secure random key
token = secrets.token_urlsafe(32)  # Secure session token
# Or use OS random source directly:
import os
key = os.urandom(16)

Why this works:

  • Uses cryptographically secure pseudo-random number generator (CSPRNG) with sufficient entropy
  • Prevents attackers from predicting session tokens, encryption keys, or security tokens
  • secrets module (Python) uses OS-level randomness (/dev/urandom, CryptGenRandom) designed for security
  • Blocks brute-force attacks on weak session IDs or predictable password reset tokens
  • Essential for generating encryption keys, nonces, salts, authentication tokens that resist prediction

Additional Resources