현재 진행 중인 프로젝트에서 `Sparrow`를 사용해 웹 취약점 진단을 실시했다.
Math.random() 사용이 지적을 받았다. 왜 Math.random()이 보안에 취약한지 정리해 보자.
1.Math.random()
1.1 Math.random()의 정의
`Math.random()`은 `java.lang.Math` 클래스의 정적(static) 메서드로, `0.0 이상 1.0 미만`의 범위에서 균등하게 분포된 난수를 생성하여 반환한다.
1.2 Math.random()의 특징
1) 반환 타입 : `double`
2) 특징 : 생성되는 값은 `균등 분포(uniform distribution)`를 따르기 때문에 모든 값이 동일한 확률로 생성된다.
1.3 예제(주사위 게임)
코딩을 처음 배울 때 배웠을만한 주사위 게임 예제 코드이다.
public class DiceGame {
public static void main(String[] args) {
// Math.random()을 사용하여 1부터 6까지의 난수 생성
int dice = (int)(Math.random() * 6) + 1;
// 생성된 주사위 숫자 출력
System.out.println("당신의 주사위는 : " + dice);
}
}
이 코드를 실행하면 1부터 6 사이의 숫자 중 하나를 출력한다.
매우 간단하기 때문에 랜덤 한 요소가 필요한 기능들을 구현할 때 많이 사용한다.
2. Math.random()의 취약성
Math.random()은 잠깐 봤을 때는 랜덤한 결괏값을 반환해 주는 것처럼 보이지만 자세히 들여다보면 어떤 규칙성을 발견할 수가 있다고 한다. 인간의 관점에서 이것은 큰 위험이 아니지만, 계산이 빠른 컴퓨터에게는 아주 허술한 빈틈일 수 있겠다.
비밀번호의 자동 생성기능이나 복권의 난수 생성이 Math.random()으로 만들어져 있어 규칙성을 가지고 있었다면 얼마나 큰일인가?
2.1 Math.random()의 동작 방식
`Math.random()`은 `의사 난수 생성기(Pseudorandom Number Generator, PRNG)`를 사용해 난수를 생성한다.
PRNG는 알고리즘을 기반으로 난수를 생성하는데, 동일한 초기 시드 값을 사용하면 동일한 난수 시퀀스를 생성할 수 있다.
현재 사용하고 있는 java8 버전에서 Math.random()을 쭉 타고 들어가다 보면 Random 클래스를 사용하고 있는 것을 확인할 수 있다.
Random 클래스는 선형 합동 생성기(Linear Congruential Generator, LCG)를 사용하고 있는데 이름 그래로 선형 합동 생성기는 선형적이므로, 초기 시드가 알려지면 난수를 예측할 수 있다.
2.2 Math.random()의 규칙성 예시
public class RandomTest{
public static void main(String[] args){
// 동일한 시드로 두 개의 Random 인스턴스 생성
Random rand1 = new Random(123456789);
Random rand2 = new Random(123456789);
for (int i = 0; i < 5; i++) {
System.out.println("랜덤1: " + rand1.nextInt(100));
System.out.println("랜덤2: " + rand2.nextInt(100));
}
}
}
Random 클래스를 사용해 난수를 생성할 때 같은 시드를 사용할 경우 같은 규칙성을 가진 난수를 생성하는 것을 알 수 있다.
3. 해결법
3.1 SecureRandom의 사용
public class SecureDiceGame {
public static void main(String[] args) {
// SecureRandom을 사용하여 1부터 6까지의 난수 생성
SecureRandom secureRandom = new SecureRandom();
int dice = secureRandom.nextInt(6) + 1;
// 생성된 주사위 숫자 출력
System.out.println("당신의 주사위는 : " + dice);
}
}
기존의 Math.random()을 사용하던 코드에서
SecureRandom의 인스턴스를 만들어준 뒤 nextInt를 사용하기만 하면 된다!
사실 겉으로 보기에는 전혀 차이가 없다.
그리고 사용이 어려운 것도 아니다.
그러니 무지성으로 secureRandom을 사용하면 되는 걸까??
아니다.
4. SecureRandom
4.1 SecureRandom 클래스란?
`SecureRandom`은 `java.security`패키지에 속한 클래스로, 암호학적으로 안전한 난수를 생성하기 위해 설계되었다. `Random` 클래스는 빠르고 간단한 난수 생성을 제공하지만, 보안적인 측면에서는 취약할 수 있다. 반면, `SecureRandom`은 예측 불가능하고 높은 수준의 무작위성을 보장하기 때문에 보안에 민감한 애플리케이션에 적합하다.
4.2 SecureRandom의 장단점
장점
1. 보안성
암호학적으로 안전한 난수를 생성하여, 예측 불가능성과 높은 무작위성을 보장한다고 한다.
2. 다양한 난수 생성 기능
다양한 타입의 난수를 생성할 수 있다.
3. 유연한 시드 관리
시드 관리를 알아서 해주니까 편하다.
4. 표준 라이브러리
Java 표준 라이브러리에 포함되어 있기 때문에 추가적인 설치와 설정이 필요 없다.
단점
1. 성능
체감할 수 있는 유일한 단점이 아닐까? 복잡한 내부로직을 가졌는데도 빠를 수 있다면 그건 말이 안 될 것이다.
그렇기 때문에 굳이 보안성이 필요 없는 곳까지 무조건 사용할 필요는 없는 것이다.
보안도 물론 중요하지만, 한국인에게 속도는 보안에 버금가는 중요 덕목이 아닐까?
5. SecureRandom과 Math.random() 비교
`SecureRandom`에 대해 정리하며 `SecureRandom`이 만능이 아니라는 것을 알았다.
둘을 비교하며 어떻게 사용해야 할지 정리해 보며 마무리하도록 하자.
특징 | `Math.random()` | `SecureRandom` |
기반 클래스 | `java.util.Random` | `java.security.SecureRandom` |
난수 생성 방식 | 의사 난수 생성기(PRNG), 선형 합동 생성기(LCG) | 암호학적으로 안전한 PRNG(CSPRNG) |
예측 가능성 | 높음 | 낮음 |
보안 용도 적합성 | 부적합 | 적합 |
시드 설정 가능성 | 가능하지만 보안 취약 | 가능하지만 비권장 |
사용 용도 | 간단한 난수 필요 시 (예: 주사위 게임 등) | 암호화 키 생성, 세션 토큰, 보안 관련 난수 필요 |
성능 | 빠름 | 비교적 느림 |
'JAVA' 카테고리의 다른 글
[Java] replaceAll() - 자바 특정 문자 변경 함수 (4) | 2024.07.23 |
---|---|
[자바 디자인 패턴]의 싱글톤 패턴이란? (1) | 2024.01.15 |
[JAVA] VO(DTO) LIST를 특정 필드값으로 정렬하기 + 형변환 (0) | 2023.08.10 |