PRNG faible

Catégorie OWASP : MASVS-CRYPTO : cryptographie

Présentation

Un générateur de nombres pseudo-aléatoires (ou PRNG) est un algorithme qui génère des séquences de nombres prévisibles en fonction d'une valeur de départ appelée graine. Une séquence de nombres générés par un PRNG a presque les mêmes propriétés qu'une séquence de nombres véritablement aléatoires, mais est plus rapide et moins coûteuse en calcul à créer.

En d'autres termes, les PRNG ont des assurances plus élevées que les RNG faibles (par exemple, java.math.Random) en termes d'uniformité de la distribution d'entropie, qui émulent des séquences de nombres véritablement aléatoires. La génération de nombres véritablement aléatoires nécessite des équipements spécialisés et sort souvent du champ d'application du développement normal. Cet article ne traite pas de la génération de nombres véritablement aléatoires. Il se concentre uniquement sur les PRNG, qui constituent la méthodologie standard utilisée.

Des failles de PRNG faible se produisent lorsque les développeurs utilisent un PRNG standard à des fins de chiffrement, plutôt qu'un CSPRNG, qui est sécurisé du point de vue cryptographique. Les exigences relatives aux CSPRNG sont plus strictes et, lorsque la valeur de graine n'est pas connue, ils ne doivent offrir aux pirates informatiques qu'un avantage insignifiant pour différencier une séquence de sortie d'une séquence aléatoire réelle.

Les pirates informatiques peuvent également deviner la séquence de nombres générés lorsque des graines prévisibles, telles que celles codées en dur par le développeur, sont utilisées pour initialiser la génération de nombres via un PRNG ou un CSPRNG, car ils peuvent deviner la graine et ainsi prédire la sortie générée par le PRNG.

Impact

Si un PRNG non sécurisé de manière cryptographique est utilisé dans un contexte de sécurité tel que l'authentification, un pirate informatique peut deviner les nombres générés aléatoirement et accéder à des données ou à des fonctionnalités privilégiées.

Stratégies d'atténuation

Général

java.security.SecureRandom

Recommandé pour des raisons de sécurité. Si la version de noyau Linux est 5.17 ou une version ultérieure, ou si le blocage du thread est acceptable, attendez que l'entropie suffisante s'accumule avant de générer les nombres aléatoires (par exemple, utilisez /dev/random). Pour ce faire, appelez getInstanceStrong() :

Kotlin

val rand = SecureRandom.getInstanceStrong()

Java

SecureRandom rand = SecureRandom.getInstanceStrong();

Sinon, sur les versions de noyau Linux antérieures à la version 5.17 où le blocage du thread n'est pas acceptable lors de la génération de nombres aléatoires, le constructeur SecureRandom doit être appelé directement :

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 obtient la valeur de graine par défaut à partir de /dev/urandom et est automatiquement utilisé lorsque l'objet est construit ou obtenu. Il n'est donc pas nécessaire d'alimenter explicitement le PRNG. En règle générale, toute utilisation déterministe de SecureRandom est déconseillée (surtout si cela entraîne le codage en dur d'une valeur de graine, que toute personne décompilant l'application peut voir). Les développeurs qui souhaitent générer des résultats pseudo-aléatoires reproductibles doivent utiliser des primitives plus appropriées, telles que HMAC, HKDF, SHAKE, etc.

java.util.Random

À éviter à des fins de sécurité/d'authentification, mais acceptable dans d'autres circonstances.

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

Ressources