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
secretsmodule oros.urandom()instead ofrandom - Java: Use
java.security.SecureRandominstead ofjava.util.Random - JavaScript/Node.js: Use
crypto.randomBytes()orcrypto.getRandomValues()instead ofMath.random() - .NET: Use
System.Security.Cryptography.RandomNumberGeneratorinstead ofSystem.Random - PHP: Use
random_bytes()orrandom_int()instead ofrand()ormt_rand()
- Python: Use
- Avoid standard PRNGs for security purposes:
random,Math.random(),java.util.Randomare 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
randomwithsecrets- 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
secretsmodule,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
randomorMath.randomfor 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
secretsmodule (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