Weak PRNG

OWASP category: MASVS-CRYPTO: Cryptography

Overview

A pseudorandom number generator (PRNG) is an algorithm that generates predictable number sequences based on a starting value called a seed. A PRNG-generated number sequence has approximately the same properties as a truly random number sequence, but is faster and less computationally expensive to create.

In other words, PRNGs have higher assurances than weak RNGs (e.g. java.math.Random) in terms of evenness of entropy distribution, which emulate truly random number sequences. Truly random number generation requires specialized equipment and is often outside the scope of normal development. This article does not cover truly random number generation, and focuses only on PRNGs as they are the standard methodology in use.

Weak PRNG vulnerabilities occur when developers use a regular PRNG for cryptographic purposes, instead of a cryptographically-secure PRNG (CSPRNG). CSPRNGs have stricter requirements, and when the seed is unknown, they must give an attacker only an insignificant advantage in differentiating an output sequence from an actual random sequence.

Attackers may also be able to guess the generated number sequence when predictable seeds – such as those hardcoded by the developer – are used to initialize a PRNG or CSPRNG, as the attacker can guess the seed and thus predict the output generated by the PRNG.

Impact

If a non-cryptographically secure PRNG is used in a security context like authentication, an attacker may be able to guess the randomly-generated numbers and gain access to privileged data or functionality.

Mitigations

General

java.security.SecureRandom

Recommended for security uses. If the Linux kernel version is 5.17+ or blocking the thread is acceptable, wait for enough entropy to accumulate before you generate the random numbers (i.e. use /dev/random). To do so, call getInstanceStrong():

Kotlin

val rand = SecureRandom.getInstanceStrong()

Java

SecureRandom rand = SecureRandom.getInstanceStrong();

Otherwise, on Linux kernel versions prior to 5.17 when blocking the thread is unacceptable when generating random numbers, then the SecureRandom constructor should be called directly:

Kotlin

import java.security.SecureRandom

object generateRandom {
    @JvmStatic
    fun main(args: Array<String>) {
        // Create instance of SecureRandom class
        val rand = SecureRandom()

        // Generate random integers in range 0 to 999
        val rand_int = rand.nextInt(1000)

        // Use rand_int for security & authentication
    }
}

Java

import java.security.SecureRandom;

public class generateRandom {

    public static void main(String args[])
    {
        // Create instance of SecureRandom class
        SecureRandom rand = new SecureRandom();

        // Generate random integers in range 0 to 999
        int rand_int = rand.nextInt(1000);

        // Use rand_int for security & authentication
    }
}

SecureRandom gets the default seed from /dev/urandom, and is automatically used when the object is constructed or obtained, so there is no need to explicitly seed the PRNG. In general, any deterministic usage of SecureRandom is discouraged (especially if this leads to hardcoding a seed value, which anyone decompiling the app can see). Developers who want to generate reproducible pseudorandom output should use more appropriate primitives such as HMAC, HKDF, SHAKE, etc.

java.util.Random

Avoid for security / authentication purposes, acceptable to use for anything else.

Kotlin

import java.util.Random

object generateRandom {
    @JvmStatic
    fun main(args: Array<String>) {
        // Create instance of SecureRandom class
        val rand = Random()

        // Generate random integers in range 0 to 999
        val rand_int = rand.nextInt(1000)
    }
}

Java

import java.util.Random;

public class generateRandom {

    public static void main(String args[])
    {
        // Create instance of Random class
        Random rand = new Random();

        // Generate random integers in range 0 to 999
        int rand_int = rand.nextInt(1000);
    }
}

Resources