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


202

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

이 토큰의 목적은 상당히 민감합니다. 세션 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시작시 한 번입니다.


14
시드되면 java.util.Random의 출력은 결정적인 숫자 시퀀스입니다. 당신은 그것을 원하지 않을 수 있습니다.
피터 Štibraný

1
코드 Random는 시작할 때 한 번만 시드합니까 , 아니면 모든 토큰에 대해 새로운 시드를 줍니까? 잘만되면, 이것은 멍청한 질문이지만, 내가 확인할 것이라고 생각했습니다.
Tom Anderson

8
랜덤은 48 비트의 내부 상태를 가지며 nextLong 2 ^ 48 () 호출 가능한 모든 생산되지 않음을 의미한다 반복 후에 long또는 double값.
피터 로리

3
또 다른 심각한 문제가 있습니다. 64 비트는 1.84 * 10 ^ 19의 가능한 조합을 의미하며 정교한 공격에는 견딜 수 없습니다. 60 시간 동안 초당 90 * 10 ^ 9 키로 56 비트 DES 코드 (요인 256 이하)를 해독 한 머신이 있습니다. 128 비트 또는 두 개의 long을 사용하십시오!
Thorsten S.

답변:


232

표준 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합니다. 사용 방식을 변경하는 것에 대해 영리하면 거의 항상 덜 안전합니다.


4
SecureRandom의 작동 방식을 설명하는 데 도움이 될 수 있습니다 (랜덤 작동 방식을 설명하는 것처럼).
gresdiplitude

4
이는 secureRandom의 목적을 상실합니다
Azulflame

나는 그 교훈을 어려운 방법으로 배웠습니다. 그러나 강력한 사이퍼와 찾기 어려운 소스는 잘 작동합니다. 노치는 그에 대해 배울 수있다 (그는 "passwordfile"을 키로 사용하여 기본 암호화로 인코딩 된 .lastlogin 파일에 사용자의 비밀번호를 인코딩한다)
Azulflame

1
실제 질문 : 자바가 비슷한 API로 더 안전한 prng을 생성 할 수 있다면 왜 깨진 것을 대체하지 않았습니까?
Joel Coehoorn

11
@JoelCoehoorn 그것은 Random깨지지 않았습니다 -그것은 다른 시나리오에서 사용해야합니다. 물론 항상 SecureRandom을 사용할 수 있습니다. 그러나 일반적으로 SecureRandom순수보다 눈에 띄게 느립니다 Random. 좋은 통계적 속성과 우수한 성능에만 관심이 있지만 실제로 보안에 신경 쓰지 않는 경우가 있습니다. Monte-Carlo 시뮬레이션이 좋은 예입니다. 나는 비슷한 대답으로 그것에 대해 의견을 썼다 . 아마도 유용 할 것이다.
엠 보스

72

임의의 비트는 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 인스턴스 이 두 가지 병렬 처리를 실행하면 운영 체제에서 얻은 엔트로피를 소진하지 않으면서도 실제 임의의 값으로 정기적으로 시드 할 수 있습니다.


를 예측하는 데 2 ​​^ 48 미만이 필요하므로 RandomOP는 전혀 사용하지 않아야 Random합니다.
엠 보스

@ emboss : 나는 무차별 대결에 대해 이야기하고 있습니다.
Ashwin

1
리눅스에주의를 기울이십시오 : 엔트로피 소모에 도달 할 수 있습니다 (하드웨어보다 VM에서 더 많음)! 에서 봐 /proc/sys/kernel/random/entropy_avail일부 스레드 및 검사에 읽을 때 너무 오래 기다려야에는이 있음을 덤프하지/dev/random
이브 마틴

2
Oracle JRE (최소 1.7)는 / dev / random이 아닌 기본적으로 / dev / urandom과 함께 작동하므로 답변의 접미사가 더 이상 올바르지 않습니다. securerandom.source 등록 정보에 대한 $ JAVA_HOME / lib / security / java.security 점검
Boaz

1
우리의 java.security 파일은 file : /// dev / urandom 대신 securerandom.source = file : / dev / urandom (파일 프로토콜 콜론 뒤에 슬래시 두 개, 파일 시스템 루트에 슬래시 두 개)이있어 다시 떨어졌습니다. 엔트로피 풀 소진 문제를 일으킨 / dev / random. 편집 할 수 없으므로 앱 시작시 java.security.egd 시스템 속성을 올바른 것으로 설정해야했습니다.
maxpolk 2016 년

11

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

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


11
PRNG의 두 인스턴스를 가져와 같은 값으로 시드하면 SecureRandom을 사용해도 항상 같은 난수를 얻습니다. 모든 PRNG는 결정적이므로 시드를 알고 있으면 예측할 수 있습니다.
Robert

1
다른 SecureRandom 구현이 있으며 일부는 PRNG이며 일부는 그렇지 않습니다. 반면에 java.util.Random은 항상 PRNG입니다 (Javadoc에 정의 된대로).
Peter Štibraný

3

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

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

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

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


나는 것을 주장 원래의 질문 믿지 않는 java.util.Random구현이 사용 SecureRandom내부를, 그것은 말했다 자신의 코드 가 사용하는 SecureRandom종자 Random. 아직도, 나는 지금까지 두 대답에 동의합니다. SecureRandom명시 적으로 결정적인 솔루션을 피하기 위해 사용 하는 것이 가장 좋습니다 .
Palpatim

2

현재 참조 구현은 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 비트가 음수이면 약간의 노력이 필요하지만,이를 알아낼 수 있습니다.


옳은. jazzy.id.au/default/2010/09/20/… 에서 java.util.random을 빠르게 크래킹하는 방법을 보십시오 !
ingyhere

2

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

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

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

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


0

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.