수 많은 우문은 현답을 만든다

보장된 난수 생성하는 방법 본문

개발지식/Linux

보장된 난수 생성하는 방법

aiden.jo 2022. 6. 7. 17:48

우리는 보통 난수를 생성할때 Random() 을 사용합니다.


그러나 Random() 함수는 치명적인 문제가 있어서 운영 프로그램을 짤때는 SecureRandom()을 사용해야한다는 것을 알게되어 공유하고자 합니다. 스프링 부트를 이용해서 웹 애플리케이션을 만들 때 우리는 기본적으로 톰캣을 이용합니다. 그리고 톰캣이 리눅스 환경에서 난수를 생성할 수 있도록 다음 옵션을 줘야합니다.

JAVA_OPTS='-Djava.security.egd=file:///dev/urandom



실제로 Random() 함수를 사용하면 소나큐브에서 아래와같은 Critical 버그가 발생합니다.

Random 값이 필요할 때마다 새 개체를 만드는 것은 비효율적이며
JDK에 따라 임의의 숫자가 생성되지 않을 수 있습니다.




 

무심코 사용했던 Random 함수가 어떤 문제가 있는걸까요?

기본적으로 Random함수는 우리가 생각했던 것 처럼 무작위 값을 생성하는 것이 아니라 '의사난수'를 생성합니다. '의사난수'란 난수처럼 보이게 하기 위해 어떠한 알고리즘을 사용한 규칙적인 난수를 의미합니다. 즉, 진짜 난수가 아닌 규칙적으로 난수처럼 보이는 값을 의미합니다. Random 함수의 내부를 살펴보면, 기준값이 되는 seed 를 사용해서 의사난수를 생성하고 있습니다. seed 값으로 현재시간을 사용하는데, 만약 수백만건의 메세지를 보내는 서비스에서 메세지별로 난수로 uid 를 생성한다고 하면 중복이 발생할 가능성이 큽니다.



 

그럼 SecureRandom 함수는 왜 안전할까요?

Random 클래스에는 48 비트만 있는 반면 SecureRandom은 최대 128 비트를 포함 할 수 있어서 해킹에 덜 취약합니다. 또한 Random은 시스템 시간을 시드로 사용하기에 공격자가 시드 시간을 알고 있으면 쉽게 재현 할 수 있지만 SecureRandom은 리눅스 커널의 난수발생기(PRNG : pseudo-randum number generator) 를 시드로 사용합니다. PRNG는 유사난수를 발생시키고 이를 랜덤한 seed로 사용하여 주기적으로 PRNG의 status를 갱신합니다. 이를 통해 지속적으로 새로운 유사난수를 생성할 수 있는 것입니다.

 



Entropy

엔트로피는 TCP Sequence 같이 난수가 필요한 OS나 Application에서 사용하기 위해 임의에 무작위 데이터를 수집하는것을 의미합니다. 사용자의 마우스, 키보드 I/O, Events, Interrupts, IDE 같은 다양한 Entropy Sources를 통해 무작위에 데이터를 긁어다가 input pool에 저장합니다. PRNG에는 아래와 같이 3개의 Entropy Pool이 있습니다.

- input pool :  마우스, 키보드 I/O, Events, Interrupts, IDE 등으로 부터 수집

- blocking pool :

- nonblocking pool :


* input pool에 저장된 데이터는 PRNG 내부 로직에 맞게 재배치, 해시화, XOR 등 암호학적으로 안전하게 데이터를 재생성된 후 blocking, nonblocking pool로 추출됩니다.
* 이런 방법으로 생성된 blocking pool과 nonblocking pool에서 데이터를 가져다 사용한게 바로 /dev/random과 /dev/urandom

 



결론적으로, 다양한 상황적 제약 등을 고려했을때 /dev/urandom을 사용하는게 안전하다고 합니다.






참고

- https://kdhyo98.tistory.com/m/48

- https://ksg97031.medium.com/%EB%A6%AC%EB%88%85%EC%8A%A4%EC%97%90%EC%84%9C-%EB%82%9C%EC%88%98%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%83%9D%EC%84%B1%ED%95%A0%EA%B9%8C-faf1cac14046