弱い PRNG

OWASP カテゴリ: MASVS-CRYPTO: 暗号

概要

疑似乱数ジェネレータ(PRNG)とは、シードと呼ばれる開始値に基づいて予測可能な数列を生成するアルゴリズムのことです。PRNG によって生成される数列は、真の乱数列とおおよそ同じプロパティを持ちますが、より高速に、より低い計算コストで生成できます。

言い換えると、PRNG は、弱い RNG(java.math.Random など)と比べてエントロピー分布の均一性の点で高い信頼性を持ち、真の乱数列をエミュレートできます。真の乱数を生成するには専用の機器が必要であり、多くの場合、これは通常の開発の範囲外です。この記事では真の乱数生成については説明しません。実際に使用されている標準の手法である 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();

Linux カーネル バージョンが 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);
    }
}

参考資料