Vulnerability class

Weak randomness in Python

Weak randomness is a flaw where a value that has to be unguessable, a session token, a password-reset link, an API key, is generated by a random source that is not meant for security, so an attacker can predict or reproduce it.

CWE
CWE-330, CWE-338
OWASP
A04:2025 Cryptographic Failures
Tag
predictable token
On this page

How it shows up in Python

Python has two random sources and they look almost the same. The random module is a Mersenne Twister: fast, statistically good, and fully deterministic once its internal state is known, which is recoverable from enough outputs. It is right for simulations and sampling, and wrong for anything an attacker benefits from guessing. The trap is using it for a secret:

import random
import string

def reset_token(n=12):
    return "".join(random.choice(string.ascii_letters + string.digits) for _ in range(n))

A reset token from random can be predicted by an attacker who has seen other outputs, which lets them forge a link for someone else's account. The fix is the secrets module, which draws from the operating system's cryptographically secure generator:

import secrets

def reset_token(n=32):
    return secrets.token_urlsafe(n)

Use secrets.token_urlsafe, secrets.token_hex or secrets.choice for anything security-sensitive, and make tokens long enough (32 bytes is a reasonable floor). The same trap hides in UUIDs: uuid.uuid4() is fine because it uses the secure generator, but uuid.uuid1() is built from the host and a timestamp and is partly predictable, so it should not be used as a secret. Temporary file names are a related case: a predictable name invites a race where an attacker pre-creates the path. Use tempfile.mkstemp or NamedTemporaryFile, which create the file atomically, rather than tempfile.mktemp, which only returns a name.

Why it is hard to see

  • The two APIs look identical. random.choice and secrets.choice read the same; whether a given call is a bug depends on what the value is for, which the surrounding code rarely states in a way a scanner can read.
  • Generated and used far apart. The value is produced by a helper in one module and used as a token in another. A rule that flags the random call cannot see that its output becomes a secret; a rule that flags the token cannot see where it came from.
  • It is never an exception. A predictable token works perfectly; nothing fails, so dynamic and behavioural tools have no event to catch until the token is abused.

What the es-python event looks like

With the weak-random source on, es-python marks values from the insecure generator, random.getrandbits (which the rest of the random module is built on, so randrange, randint, choice, shuffle, sample and randbytes are all covered) and uuid1. uuid4 is deliberately not marked, because it uses the secure generator. Like hardcoded credentials, the finding fires where the value is used, not where it was generated: a predictable token event means a value from a non-secure source reached a place that needs to be unpredictable, and it carries the predictable token impact tag. A predictable temporary file name is reported as predictable temp.

Because the value is used as a secret only once but may be generated far away, reporting at the point of use is what connects the two: the event's trace shows the random call that produced it. Under a Raise rule, the use raises a RuntimeError. This is an information-class finding rather than an injection, so it is never suppressed on the value: no encoding makes a predictable token unpredictable.

How to fix and prevent it

  1. Use the secrets module for every value that must be unguessable: tokens, reset links, API keys, salts for one-time use.
  2. Keep random for simulations, sampling and jitter, not for secrets.
  3. Use uuid.uuid4() rather than uuid1() where a UUID stands in for a secret, and make tokens long enough.
  4. Create temporary files with mkstemp or NamedTemporaryFile, not mktemp.
  5. In Report mode, watch the predictable token tag to find insecure random output being used where it should not be.

Classification

Weak randomness is CWE-330, Use of Insufficiently Random Values, and the PRNG case is CWE-338, Use of a Cryptographically Weak PRNG. OWASP maps both to A04:2025 Cryptographic Failures in the Top 10 for 2025.

Questions people ask

The questions that come up on forums and in reviews about this class, each answered in a few sentences.

Is random.random safe for tokens?

No: the random module is a Mersenne Twister, a generator built for simulations, and an attacker who sees enough of its outputs can reproduce every later one, so a session token, reset link or API key drawn from it is predictable. The same applies to randint, choice, shuffle, randbytes and to uuid1, which is built from the clock and the MAC address. Use the secrets module for anything that must be unguessable.

What should I use instead of random for security?

The secrets module: secrets.token_hex() or secrets.token_urlsafe() for tokens and links, secrets.choice() for picking from a set, and secrets.compare_digest() when checking a token. uuid.uuid4() is also drawn from the secure generator, unlike uuid1(). Both are backed by the operating system's random source, so there is nothing to seed and nothing to reproduce.

How does es-python report weak randomness?

With the weak-random source on, it marks values from random.getrandbits() and everything built on it, and from uuid1(), and reports where such a value is used as a secret: a predictable token event, with the predictable token impact tag, or predictable temp for a temporary file name. The trace shows the random call that produced the value, so the generation and the use are connected. The event is never suppressed, because no encoding makes a predictable value unpredictable.

Watch for Weak randomness in your own code

es-python runs your Python application unchanged and reports when untrusted data reaches the operation, with where the data came from and the line that used it. Book a live demo to see it on a real application.

EyalSec Pricing Docs Security Research Vulnerabilities Security glossary About Contact עברית Login Book a live demo