skip to content

Why is java.util.Random (or Math.random()) unsafe for generating security values like tokens or keys, and what should you use instead?

level: juniorimportance: must knowfreq 78%

answer

  1. util.Random = 48-bit LCG, crackable from 2 outputs
  2. Math.random() is the same algorithm
  3. SecureRandom = CSPRNG seeded from OS entropy
  4. 'If it protects something, use SecureRandom'
  5. SecureRandom extends Random -> drop-in

basics

~10 s

java.util.Random and Math.random() are predictable: from a few outputs an attacker can compute the seed and predict all future values. For anything security-related (tokens, keys, salts) use java.security.SecureRandom, which is unpredictable.

solid answer

~40 s

java.util.Random is a linear congruential generator (LCG) with a 48-bit seed; given a couple of outputs an attacker can recover the internal state and predict every future and past value. Math.random() is backed by the same algorithm. That predictability is fatal for session tokens, password-reset tokens, API keys, salts, IVs, and nonces, because an attacker who predicts them can forge or guess them. The correct tool is java.security.SecureRandom, a cryptographically secure PRNG (CSPRNG): it is seeded from OS entropy and designed so that observing past outputs does not let you predict future ones. Rule of thumb: if the value protects something, use SecureRandom; java.util.Random is fine only for non-security uses like shuffling a game or sampling.

go deeper

for a junior

Knows the rule: use SecureRandom for tokens/keys/passwords, not Random or Math.random(), and that SecureRandom is built into the JDK.

for a middle

Can explain that util.Random is a 48-bit LCG that is predictable from a couple of outputs and that Math.random() shares it; lists concrete vulnerable values (tokens, salts, IVs).

for a senior

Articulates the CSPRNG unpredictability property (can't predict forward or backward), names the OS entropy seeding, and can audit a codebase for misuse.

for a principal

Sets org-wide guidance/linters (e.g. ban java.util.Random in security packages), reasons about entropy sources and FIPS requirements, and weighs SecureRandom usage in threat models.

## The problem: predictability A **random number generator** in software is almost always a **PRNG** (pseudo-random number generator): a deterministic algorithm that, starting from an initial value called a **seed**, produces a long sequence of numbers that *look* random. Because it is deterministic, the entire sequence is fully determined by the seed and the algorithm. `java.util.Random` uses a **linear congruential generator (LCG)**: each step computes `state = (state * a + c) mod 2^48` and returns bits of `state`. The key facts: - The seed/internal state is only **48 bits**. - The update rule is public and simple. This means an attacker who sees a small number of outputs can solve for the internal state and then **reproduce every future and past output**. There are well-known, fast techniques (and ready-made tools) to crack `java.util.Random` from just two consecutive `nextInt()` results. `Math.random()` is just a shared `java.util.Random` under the hood, so it has the exact same weakness. ## Why that matters for security Many security values must be **unguessable**: - **Session tokens / password-reset tokens** — guess one and you hijack an account. - **API keys, CSRF tokens, nonces** — predictability defeats their purpose. - **Cryptographic keys, IVs, salts** — predictable values break the encryption or hashing that relies on them. If any of these come from `java.util.Random`, an attacker who collects a few values can predict the rest. This is a real, exploited class of vulnerability, not a theoretical one. ## The fix: a CSPRNG A **CSPRNG** (cryptographically secure PRNG) is a PRNG designed so that, even seeing many past outputs, you **cannot predict the next output** better than guessing, and you cannot run it backwards to recover earlier outputs. In Java this is `java.security.SecureRandom`. Key differences from `java.util.Random`: - It is seeded from the **operating system's entropy source** (e.g. `/dev/urandom` on Linux, the OS CSPRNG on Windows), not a fixed or time-based value. - It has a much larger internal state and a cryptographic algorithm, so its output is not invertible to a small seed. - `SecureRandom extends java.util.Random`, so it has the same methods (`nextInt`, `nextBytes`, `nextLong`…), making it a drop-in replacement. ## Practical rule > If the value protects, authenticates, or hides something, generate it with `SecureRandom`. Use `java.util.Random`/`Math.random()` only for non-security purposes (games, simulations, load balancing, sampling). For filling a buffer (token/key/salt bytes) use `secureRandom.nextBytes(buf)`. Never wrap or seed it from `System.currentTimeMillis()` — that reintroduces predictability.

  • Roughly how many outputs does an attacker need to predict a java.util.Random sequence?
    Very few — typically just two consecutive outputs are enough to recover the 48-bit internal state and then reproduce all subsequent (and prior) values.
  • Is SecureRandom a subclass of java.util.Random?
    Yes. SecureRandom extends java.util.Random, so nextInt/nextLong/nextBytes etc. are available and it can replace Random directly in security code.

saying these in an interview costs you the question

  • Saying java.util.Random is fine if you 'seed it well' — the algorithm itself is the weakness
  • Believing Math.random() is more secure than new Random()
  • Thinking you need a third-party library — SecureRandom is built in
  • Confusing 'looks random to me' with cryptographically unpredictable

context