Insecure generation of random numbers
Need
Use of an unseeded cryptographically secure random number generator for security values
Context
• Usage of Kotlin 1.9+ on the JVM for building application services
• Usage of java.security.SecureRandom for generating security tokens
Description
1. Non compliant code
import java.security.SecureRandom
import java.util.Base64
fun newResetToken(): String {
// A constant seed turns SecureRandom into a predictable sequence
val random = SecureRandom.getInstance("SHA1PRNG")
random.setSeed(12345L)
val bytes = ByteArray(32)...The `newResetToken` function below creates password reset tokens with a `SecureRandom` whose seed is the constant `12345L`, set with `setSeed` before the first use. `SecureRandom` is only unpredictable when it is seeded by the operating system. When code supplies the seed before the generator has produced any output, several providers, such as the `SHA1PRNG` implementation, use that value as their entire seed. The output then becomes a fixed sequence: every instance started with the same seed produces the same tokens, in the same order, on every server and after every restart. An attacker who knows or guesses the seed, which is visible in the source code, can compute the reset tokens issued to other users and take over their accounts. Creating the generator with `SecureRandom(seedBytes)` from a constant array has the same effect.
2. Steps
• Create `SecureRandom` with its default constructor and let the JDK seed it from the operating system.
• Remove every call to `setSeed` with constant or predictable values, and every `SecureRandom(bytes)` built from a fixed array.
• Do not use `java.util.Random` or `kotlin.random.Random` for tokens, keys, nonces or passwords.
• Use at least 128 bits of randomness for security tokens; 32 bytes give 256 bits.
• Reuse one `SecureRandom` instance instead of creating one for every value.
3. Secure code example
import java.security.SecureRandom
import java.util.Base64
// Seeded by the operating system; never call setSeed with a fixed value
private val random = SecureRandom()
fun newResetToken(): String {
val bytes = ByteArray(32)...The corrected code uses a single `SecureRandom` created with its default constructor and never calls `setSeed`. The JDK seeds it from the operating system entropy source, such as `/dev/urandom` on Linux, so its output cannot be predicted from the source code or from previous tokens. Each token has 32 random bytes, 256 bits of entropy, encoded as URL-safe Base64 without padding so it can be used in links. The generator is thread-safe and is created once, which avoids the cost of seeding it on every call. `SecureRandom.getInstanceStrong()` is an alternative for long-term keys, but it can block on some systems, so the default constructor is the recommended choice for tokens.
References
• 034. Insecure generation of random numbers