Safeguard
Application Security•

CWE-338 Cryptographically Weak PRNG: Use a CSPRNG for Secrets

rand(), Math.random() and mt_rand() are fine for games and unsafe for tokens. CWE-338 shows how weak generators end up in security code and how to replace them.

Safeguard Security Team
3 min read

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.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.