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
- Usa
java.security.SecureRandom
cuando haya implicaciones de seguridad. - Usa
java.util.Random
para cualquier otro caso. - Nunca uses
Math.random
.
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);
}
}