약한 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()

자바

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

자바

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

리소스