弱 PRNG

OWASP 类别:MASVS-CRYPTO:加密

概览

伪随机数生成器 (PRNG) 是一种根据称为种子的起始值生成可预测的数字序列的算法。由 PRNG 生成的数字序列在属性上与真正的随机数序列大致相同,但前者创建速度更快,计算开销也更低。

也就是说,对于模拟真正的随机数序列的熵分配,在均匀性方面,PRNG 比弱 RNG(例如 java.math.Random)更有保证。真正随机数的生成需要专门的设备,通常不在正常开发的范围之内。本文并不涵盖真正随机数的生成,仅重点介绍 PRNG,因为 PRNG 是当下使用的标准方法。

如果开发者使用常规 PRNG 进行加密,而不采用确保加密安全的 PRNG (CSPRNG),就会出现弱 PRNG 漏洞。CSPRNG 有更严格的要求,当种子未知时,它们只能让攻击者在区分输出序列与实际随机序列方面占据微乎其微的优势。

当系统使用可预测的种子(例如开发者硬编码的种子)来初始化 PRNG 或 CSPRNG 时,攻击者或许还可以猜测生成的种子序列,因为攻击者可以猜测出这些种子,从而预测 PRNG 生成的输出。

影响

如果在身份验证等安全上下文中使用无法确保加密安全的 PRNG,攻击者或许能猜出随机生成的数字,获得需要特权才能访问的数据或功能的访问权限。

缓解措施

常规

java.security.SecureRandom

用于安全用途时,建议使用。如果 Linux 内核版本为 5.17 或以上,或者可以阻止线程,可等待累积足够多的熵后再生成随机数(即使用 /dev/random)。为此,请调用 getInstanceStrong()

Kotlin

val rand = SecureRandom.getInstanceStrong()

Java

SecureRandom rand = SecureRandom.getInstanceStrong();

但是在 5.17 之前的 Linux 内核版本中,如果在生成随机数时不可阻止线程,那么应直接调用 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 会从 /dev/urandom 获取默认种子,并且系统在构建或获取对象时会自动使用它,因此无需明确获取 PRNG 的种子。一般来说,不建议对 SecureRandom 进行任何确定性的使用(如果这样做会导致对种子值进行硬编码,而任何人在解压缩该应用时都能看到该种子值,便更为如此)。如果开发者想要生成可重现的伪随机输出,应使用更合适的基元,例如 HMAC、HKDF、SHAKE 等。

java.util.Random

避免用于安全/身份验证目的,适用于任何其他用途。

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

资源