低安全性 PRNG

OWASP 類別:MASVS-CRYPTO:加密編譯

簡介

虛擬亂數產生器 (PRNG) 是一種演算法,能夠根據稱為「種子」的起始值產生可預測的數序。PRNG 產生的數序具有與真正隨機數序大致相同的屬性,但設定速度更快,設定時的運算成本也較低。

換句話說,PRNG 較有把握提供比低安全性 RNG (例如 java.math.Random) 還要高的熵發布均勻度,能夠模擬真正的隨機數序。想產生真正的隨機號碼,必須有相關的專業設備,這種產生作業通常超出一般開發範圍。本文並未介紹真正隨機號碼產生作業,而且僅著重於當做標準方法使用的 PRNG。

如果開發人員將一般 PRNG 用於加密編譯,而不是使用經過加密編譯的 PRNG (CSPRNG),就會出現低安全性 PRNG 漏洞。CSPRNG 有更嚴格的規定,且當種子不明時,CSPRNG 必須確保攻擊者僅在區分輸出內容序列和真正的隨機序列方面占有優勢,但這些優勢其實微不足道。

此外,假如可預測的種子 (例如開發人員硬式編碼的種子) 能用於將 PRNG 或 CSPRNG 初始化,由於攻擊者可以猜到種子並據此預測 PRNG 產生的輸出內容,因此也或許能夠猜出最終產生的數序。

影響

如果將未經過加密編譯且安全無虞的 PRNG 用於安全性作業 (例如驗證),攻擊者或許可以猜到隨機產生的數字,進而取得特殊權限資料或功能的存取權。

因應措施

一般

java.security.SecureRandom

建議用於安全性用途。如果 Linux kernel 為 5.17 以上版本,「或者」封鎖執行緒是可接受的做法,請等待熵累積足夠的數量,再產生隨機號碼 (也就是使用 /dev/random)。如要這麼做,請呼叫 getInstanceStrong()

Kotlin

val rand = SecureRandom.getInstanceStrong()

Java

SecureRandom rand = SecureRandom.getInstanceStrong();

但如果 Linux kernel 為 5.17 以下版本,且產生隨機號碼時不允許封鎖執行緒,那麼則可直接呼叫 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);
    }
}

資源