PRNG débil

Categoría de OWASP: MASVS-CRYPTO: Criptografía

Descripción general

Un generador de números seudoaleatorio (PRNG) es un algoritmo que crea secuencias de números predecibles basadas en un valor inicial denominado origen. Una secuencia de número generada por PRNG tiene aproximadamente las mismas propiedades que una secuencia de número verdaderamente aleatoria, pero es más rápida y menos costosa de crear.

En otras palabras, los PRNG tienen más garantías que los RNG débiles (p. ej., java.math.Random) en cuanto a la uniformidad de la distribución de la entropía, que emula secuencias de números realmente aleatorios. La generación de números realmente aleatorios requiere equipos especializados y suele estar fuera del alcance del desarrollo normal. Este artículo no abarca la generación de números realmente aleatorios y se enfoca solo en los PRNG, ya que son la metodología estándar que se utiliza.

Las vulnerabilidades de PRNGs débiles se producen cuando los desarrolladores usan una PRNG normal con fines criptográficos, en lugar de una PRNG criptográficamente segura (CSPRNG). Los CSPRNG tienen requisitos más estrictos y, cuando se desconoce el origen, deben darle a un atacante solo una ventaja insignificante en diferenciar una secuencia de salida de una secuencia aleatoria real.

Es posible que los atacantes también puedan adivinar la secuencia de números generados cuando se usan orígenes predecibles, como los codificados por el desarrollador, para inicializar un PRNG o CSPRNG, ya que el atacante puede adivinar el origen y, por lo tanto, predecir el resultado que genera el PRNG.

Impacto

Si se usa un PRNG no seguro a nivel criptográfico en un contexto de seguridad, como la autenticación, un atacante puede adivinar los números generados de forma aleatoria y obtener acceso a datos o funciones con privilegios.

Mitigaciones

General

java.security.SecureRandom

Recomendado para usos de seguridad. Si la versión de kernel de Linux es la 5.17 o una posterior, o bien si bloquear el subproceso es aceptable, espera a que se acumule suficiente entropía antes de generar los números aleatorios (es decir, usar /dev/random). Para hacerlo, llama a getInstanceStrong():

Kotlin

val rand = SecureRandom.getInstanceStrong()

Java

SecureRandom rand = SecureRandom.getInstanceStrong();

De lo contrario, en las versiones de kernel de Linux anteriores a 5.17, cuando no se puede bloquear el subproceso al generar números aleatorios, se debe llamar directamente al constructor SecureRandom:

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 obtiene el valor predeterminado de /dev/urandom y se usa automáticamente cuando se construye o se obtiene el objeto, por lo que no es necesario propagar la PRNG de forma explícita. En general, no se recomienda el uso determinista de SecureRandom (especialmente si esto genera la codificación de un valor de origen, que puede ver cualquier persona que descompile la app). Los desarrolladores que deseen generar resultados pseudoaleatorios reproducibles deben usar primitivas más adecuadas, como HMAC, HKDF, SHAKE, etcétera.

java.util.Random

Se debe evitar para fines de seguridad y autenticación; es aceptable para todos los demás usos.

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);
    }
}

Recursos