CWE-338, Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG), applies when a product uses a PRNG in a security context but the generator's algorithm is not cryptographically strong.
It is one of the most common randomness mistakes because the weak function is usually the one easiest to find in the standard library.
How it happens
MITRE explains that a non-cryptographic PRNG in a cryptographic context exposes the cryptography to certain attacks. Such generators are often built for other purposes. A mediocre source of randomness is sometimes sufficient or even preferable, since weak generators take less processing power and do not spend the precious, finite entropy sources of a system. The weakness appears when that same function gets reused for a session ID, a reset token or a key.
// Vulnerable: Math.random is not cryptographically secure
const resetToken = Math.random().toString(36).slice(2);
Observed examples:
- CVE-2021-3692: a PHP framework used mt_rand() when generating tokens.
- CVE-2009-3278: a crypto product used the rand() library function to generate a recovery key, making brute force easier.
- CVE-2009-3238: a random number generator could repeatedly generate the same value.
What an attacker gains
If a PRNG is used for authentication and authorization, such as a session ID or a seed for generating a cryptographic key, an attacker may be able to bypass the protection mechanism.
How to find it
Static analysis can find some instances. A grep for rand, random, mt_rand, Math.random and java.util.Random in files that touch authentication, sessions, passwords or encryption is a productive start. Code review should ask of every random call: what happens if an attacker predicts this?
Try it yourself
This demo shows a static code scan picking out risky calls.
Click through it at your own pace, or open it full screen.
How to fix it
MITRE's recommended solution is to use functions or hardware that use hardware based random number generation for all crypto, naming CryptGenRandom on Windows and hw_rand() on Linux. Current operating systems expose the same idea through their secure random interfaces, and language runtimes wrap them.
- Python: secrets, os.urandom
- Node.js: crypto.randomBytes, crypto.randomUUID
- Java: java.security.SecureRandom
- PHP: random_bytes
- Go: crypto/rand
// Fixed
SecureRandom sr = new SecureRandom();
byte[] token = new byte[32];
sr.nextBytes(token);
Then keep the weak generator for what it is good at, such as simulations, shuffles and test data.
Quick check
Is UUID version 4 safe for secrets?
That depends on the implementation. Only use one backed by a secure generator, and prefer random bytes when you need a secret.
Can I make a weak generator safe by hashing its output?
No. Hashing does not add entropy. The predictable state is still there.