[java] java.util.Random과 java.security.SecureRandom의 차이점

팀에서 임의 토큰을 생성하는 일부 서버 측 코드 (자바)를 넘겨주었습니다.

이 토큰의 목적은 상당히 민감합니다. 세션 ID, 비밀번호 재설정 링크 등에 사용됩니다. 따라서 누군가 추측하거나 피할 수없는 무력을 피하기 위해 암호화 적으로 임의적이어야합니다. 토큰은 “길이”이므로 64 비트입니다.

코드는 현재 java.util.Random클래스를 사용하여 이러한 토큰을 생성합니다. 문서 에 대한 java.util.Random명확는 다음을 주장한다 :

java.util.Random의 인스턴스는 암호화 적으로 안전하지 않습니다. 보안에 민감한 응용 프로그램에서 사용할 수 있도록 암호로 안전한 의사 난수 생성기를 얻으려면 SecureRandom을 사용하는 것이 좋습니다.

그러나 코드가 현재 사용하는 방식은 다음과 같습니다 java.util.Random. java.security.SecureRandom클래스를 인스턴스화 한 다음 SecureRandom.nextLong()메소드를 사용하여 java.util.Random클래스 를 인스턴스화하는 데 사용되는 시드를 가져옵니다 . 그런 다음 java.util.Random.nextLong()메소드를 사용하여 토큰을 생성합니다.

그래서 내 질문은 지금- java.util.Random를 사용하여 시드되고 있다는 점에서 여전히 안전하지 java.security.SecureRandom않습니까? java.security.SecureRandom토큰을 생성하는 데 독점적으로 사용하도록 코드를 수정해야 합니까?

현재 코드 시드는 Random시작시 한 번입니다.



답변

표준 Oracle JDK 7 구현은 Linear Congruential Generator를 사용하여에 임의의 값을 생성합니다 java.util.Random.

임의의 값을 생성하는 java.util.Random메소드에 대한 주석 에서 소스 코드 (JDK 7u2)에서 가져옵니다 protected int next(int bits).

이것은 DH Lehmer에 의해 정의되고 컴퓨터 프로그래밍 기술, 볼륨 3 :
Seminumerical Algorithms , 섹션 3.2.1 에서 Donald E. Knuth에 의해 설명 된 선형 합동 의사 난수 생성기
입니다.

선형 합동 발생기의 예측 가능성

Hugo Krawczyk는 이러한 LCG를 어떻게 예측할 수 있는지에 대한 꽤 좋은 논문을 썼습니다 ( “동일 발전기 생성 방법”). 운이 좋고 관심이 있다면 웹에서 무료로 다운로드 할 수있는 버전을 찾을 수 있습니다. 그리고 보안 상 중요한 목적으로 LCG를 사용 해서는 안된다는 것을 분명히 보여주는 더 많은 연구가 있습니다 . 이것은 또한 임의의 숫자 예측할 수 있음을 의미하며 , 세션 ID 등에 원하지 않는 것입니다.

선형 일치 생성기를 끊는 방법

전체주기가 잘못된 후에 공격자가 LCG가 반복 될 때까지 기다려야한다는 가정. 최적의주기 (재발 관계의 계수 m)를 사용하더라도 전체주기보다 훨씬 적은 시간에 미래 값을 예측하기가 매우 쉽습니다. 결국, 그것은 해결해야 할 모듈 식 방정식 일뿐입니다 .LCG의 충분한 출력 값을 관찰하자마자 쉽게됩니다.

“더 나은”시드로 보안이 향상되지 않습니다. SecureRandom다이를 여러 번 굴려서 생성 된 임의의 값으로 시드 하거나 값을 생성하더라도 상관 없습니다 .

공격자는 단순히 관찰 된 출력 값에서 시드를 계산합니다. 의 경우 2 ^ 48보다 훨씬 적은 시간 이 걸립니다 java.util.Random. 불신자들은이 실험을 시도 할 수 있는데, 여기서 Random대략 2 ^ 16의 시간에 단 2 개의 출력 값만 관찰 하는 미래의 출력을 예측할 수 있음을 알 수 있습니다 . 현대 컴퓨터에서 지금 바로 임의의 숫자 출력을 예측하는 데 1 초가 걸리지 않습니다.

결론

현재 코드를 교체하십시오. SecureRandom독점적으로 사용하십시오 . 그러면 최소한 결과를 예측하기 어렵다는 약간의 보증이있을 것입니다. 암호로 안전한 PRNG의 속성을 원한다면 (원하는 경우), 당신은 함께 가야 SecureRandom합니다. 사용 방식을 변경하는 것에 대해 영리하면 거의 항상 덜 안전합니다.


답변

임의의 비트는 48 비트이며 SecureRandom은 최대 128 비트를 가질 수 있습니다. 따라서 보안 무작위로 반복 할 가능성은 매우 적습니다.

랜덤system clock시드로 /을 사용하여 시드를 생성합니다. 공격자가 시드가 생성 된 시간을 알면 쉽게 복제 할 수 있습니다. 그러나 SecureRandom의이 걸리는 Random Data당신의에서 os(- – 대부분의 운영 체제는 이러한 데이터를 파일에 저장 수집 그들이 키 입력 등 사이의 간격이 될 수 /dev/random and /dev/urandom in case of linux/solaris있다는 종자로 및 사용).
따라서 작은 토큰 크기가 양호하면 (임의의 경우) SecureRandom을 사용하여 시드를 생성하므로 변경없이 코드를 계속 사용할 수 있습니다. 그러나 더 큰 토큰을 원할 경우 brute force attacksSecureRandom을 사용하십시오-
임의의 2^48시도 만 필요한 경우 오늘날의 고급 CPU를 사용하면 실제 시간에 중단 할 수 있습니다. 그러나 안전한 임의의 2^128시도가 필요하며, 오늘날의 고급 시스템으로도 침입하는 데 몇 년과 몇 년이 걸릴 것입니다.

자세한 내용은 링크를 참조하십시오.

편집
@emboss가 제공하는 링크를 읽은 후에는 임의로 시드를 java.util.Random과 함께 사용해서는 안된다는 것이 분명합니다. 출력을 관찰하여 시드를 계산하는 것은 매우 쉽습니다.

SecureRandom를위한 이동 사용 – 기본 PRNG (위의 링크에 주어진)가에서 임의의 값을 사용하기 때문에 /dev/random각 호출에 대한 파일nextBytes(). 그는의 내용을 제어하지 않는 출력을 관찰 공격자가 수 없습니다이 방법은 아무것도를 만들려면 /dev/random파일을 (이 매우 가능성이) SHA1 PRNG 알고리즘을 계산 한 번만하고 VM 경우 씨가이를 이용한 달 동안 실행 시드는 수동으로 출력을 관찰하는 공격자에 의해 금이 갈 수 있습니다. 참고 -os가 임의의 바이트 (엔트로피)를에 쓸 수 있는 것보다 빠른 속도 로 호출하는 경우 NATIVE PRNG 사용시 문제가 발생할 수 있습니다 . 이 경우 SecureRandom의 SHA1 PRNG 인스턴스를 사용하고 몇 분 (또는 일부 간격)마다이 인스턴스의 값을

nextBytes()/dev/randomnextBytes()SecureRandom의 NATIVE PRNG 인스턴스 이 두 가지 병렬 처리를 실행하면 운영 체제에서 얻은 엔트로피를 소진하지 않으면서도 실제 임의의 값으로 정기적으로 시드 할 수 있습니다.


답변

java.util.Random.nextLong()동일한 시드로 두 번 실행 하면 동일한 수를 생성합니다. 보안상의 이유로 java.security.SecureRandom예측하기가 쉽지 않기 때문에 고수하고 싶습니다 .

2 개의 클래스는 비슷 합니다. 리팩토링 도구 로 변경 Random하면 SecureRandom기존 코드의 대부분이 작동 한다고 생각합니다 .


답변

기존 코드를 변경하는 것이 저렴한 작업 인 경우 Javadoc에서 제안한대로 SecureRandom 클래스를 사용하는 것이 좋습니다.

Random 클래스 구현을 발견하더라도 내부적으로 SecureRandom 클래스를 사용합니다. 다음과 같이 당연한 것으로 받아 들여서는 안됩니다.

  1. 다른 VM 구현도 마찬가지입니다.
  2. 이후 버전의 JDK에서 Random 클래스의 구현은 여전히 ​​SecureRandom 클래스를 사용합니다.

따라서 문서 제안을 따르고 SecureRandom으로 직접 이동하는 것이 더 좋습니다.


답변

현재 참조 구현은 32 비트의 현재 시드 를 직접 노출시키는 java.util.Random.nextLong()메소드 next(int)를 두 번 호출합니다 .

protected int next(int bits) {
    long nextseed;
    // calculate next seed: ...
    // and store it in the private "seed" field.
    return (int)(nextseed >>> (48 - bits));
}

public long nextLong() {
    // it's okay that the bottom word remains signed.
    return ((long)(next(32)) << 32) + next(32);
}

결과의 상위 32 비트 nextLong()는 당시의 시드 비트입니다. 시드의 너비가 48 비트 (예 : javadoc)이므로 나머지 16 비트 (65.536에 불과 함)를 반복하여 두 번째 32 비트를 생성 한 시드를 결정하면 충분합니다 *.

시드가 알려지면 다음의 모든 토큰을 쉽게 계산할 수 있습니다.

nextLong()직접 출력을 사용하면 부분적으로 PNG의 비밀을 거의 비밀로 전체 비밀을 계산할 수 있습니다. 위험한!

* 두 번째 32 비트가 음수이면 약간의 노력이 필요하지만,이를 알아낼 수 있습니다.


답변

씨앗은 의미가 없습니다. 좋은 소수 생성기는 선택한 소수에 따라 다릅니다. 모든 랜덤 생성기는 숫자에서 시작하여 ‘링’을 반복합니다. 즉, 이전 내부 값을 사용하여 한 숫자에서 다음 숫자로옵니다. 그러나 잠시 후 다시 시작에 도달하고 다시 시작합니다. 따라서 사이클을 실행하십시오. (랜덤 생성기의 반환 값은 내부 값이 아닙니다)

링을 만들기 위해 소수를 사용하는 경우 가능한 모든 번호를 완전히 순환하기 전에 해당 링의 모든 숫자가 선택됩니다. 소수가 아닌 숫자를 사용하면 모든 숫자가 선택되지 않고 사이클이 짧아집니다.

더 높은 소수는 첫 번째 요소로 다시 돌아 가기 전에 더 긴주기를 의미합니다. 따라서 안전한 임의 생성기는주기가 길어 처음에 다시 도달하기 때문에 더 안전합니다. 더 짧은주기만큼 쉽게 숫자 생성을 예측할 수 없습니다.

다른 말로 : 당신은 모두를 교체해야합니다.


답변

Random과 secureRandom의 차이점과 SecureRandom 클래스의 중요성을 쉽게 이해할 수 있도록 매우 기본적인 단어를 사용하려고합니다.

OTP (일회성 비밀번호)가 어떻게 생성되는지 궁금한 적이 있습니까? OTP를 생성하려면 Random 및 SecureRandom 클래스를 사용하십시오. 이제 OTP를 강력하게 만들기 위해 현재 기계로는 거의 불가능한 OTP를 해독하는 데 2 ​​^ 128 시도가 필요했기 때문에 SecureRandom이 더 낫습니다. 그냥 2 ^ 48 시도, 균열.