IgnatiusHeo

구현단계 보안약점 제거 기준-적절하지 않은 난수 값 사용 본문

자격/SW보안약점진단원

구현단계 보안약점 제거 기준-적절하지 않은 난수 값 사용

Ignatius Heo 2023. 7. 4. 21:02

작성일: 230704

 

※ 본 게시글은 학습 목적으로 행정안전부·KISA의 소프트웨어 보안약점 진단 가이드, 소프트웨어 개발보안 가이드를 참고하여 작성하였습니다.

 

정리 내용: 소프트웨어 보안약점 진단 가이드(364~369p)

구분 - 보안기능
설계단계 - 암호연산
https://cryptocurrencyclub.tistory.com/105
개요 예측 가능한 난수를 사용하는 것은 시스템에 보안약점을 유발한다.

예측 불가능한 숫자가 필요한 상황에서 예측 가능한 난수를 사용한다면, 공격자는 SW에서 생성되는 다음 숫자를 예상하여 시스템을 공격하는 것이 가능하다.
진단 세부사항
(설계단계)
 ① 대칭키 또는 비대칭키를 이용해서 암복호화를 수행해야 하는 경우 KISA의 암호이용안내서에서 정의하고 있는 암호화 알고리즘과 안전성이 보장되는 암호키 길이를 사용해야 한다.
  ㅇ  암복호화 기능 설계 시 안전한 암호 알고리즘을 사용하도록 설계되었는지 확인
      - 대칭키: SEED, ARIA, AES 등
      - 비대칭키: RSA, KCDSA, ECC 등
  ㅇ  암복호화 기능 설계 시 안전한 암호키 길이를 사용하도록 설계되었는지 확인
      - 대칭키: 128bit 이상
      - 비대칭키: 2048bit 이상
 
 ② 복호화되지 않는 암호화를 수행하기 위해 해시함수를 사용하는 경우 안전한 해시 알고리즘과 솔트값을 적용하여 암호화해야 한다.
  ㅇ  복호화되지 않는 암호화 기능설계시 안전한 암호화 알고리즘을 사용하도록 설계되어 있는지 확인
      - ex)SHA-2 (SHA-3는 왜 안보일까?)
  ㅇ  해시함수의 보안을 강화하기 위해 솔트값(난수발생기를 이용해 생성한 안전한 난수값)을 사용하여 암호화하도록 설계되어 있는지 확인
 
 ③ 난수 생성 시 안전한 난수 생성 알고리즘을 사용해야 한다.
  ㅇ  개발가이드에 안전한 난수 생성알고리즘을 사용하도록 코딩규칙을 정의하고 있는지 확인
      - JAVA: java.security.SecureRandom, java.util.Random
      - C: randomize(seed)
보안대책
(구현단계)

컴퓨터의 난수발생기는 난수 값을 결정하는 시드(Seed)값이 고정될 경우, 매번 동일한 난수값이 발생한다.

이를 최대한 피하기 위해 Java에서는 Random()과 Math.random() 사용 시 java.util.Random 클래스에서 기본값으로 현재시간을 기반으로 조합하여 매번 변경 되는 시드(Seed)값을 사용하며, C에서는 rand()함수 사용 시 매번 변경되는 기본 시드(Seed)값이 없으므로, srand()로 매번 변경되는 현재시간 기반 등으로 시드(Seed)값을 설정 하여야 한다.

그러나 세션 ID, 암호화키 등 보안결정을 위한 값을 생성하고 보안결정을 수행하는 경우에는, Java에서 Random()과 Math.random()을 사용하지 말아야 하며, 예측이 거의 불가능하게 암호학적으로 보호된 java.security.SecureRandom 클래스를 사용하는 것이 안전하다.

진단방법
(구현단계)

① Math.random() 메소드를 사용하는지 확인한다. Math.random()을 사용하는 경우 취약하다.

② 난수값을 세션ID로 설정하여 보안결정에 사용하는지 확인한다. 보안 결정인 경우 java.security.SecureRandom을 사용하면 안전하다.

 

다. 코드예제

 

ㅇ 분석

1: import java.util.Random;
2: ...
3: public Static int getRandomValue(int maxValue) {
//고정된 시드값을 사용하여 동일한 난수값이 생성되어 안전하지 않다.
4: Random random = new Random(100);
5: return random.nextInt(maxValue);
6: }
7: public Static String getAuthKey() {
//매번 변경되는 시드값을 사용하여 다른 난수값이 생성되나 보안결정을 위한 난수로는
안전하지 않다.
8: Random random = new Random();
9: String authKey = Integer.toString(random.nextInt());

ㅇ 내용

1. r4에서는 시드값이 고정이고

2. r8에서는 시드값을 안넣었어서 랜덤으로 생성이 가능하나 보안결정을 위한 난수로는 안전하지 않다함.

 

ㅇ 수정

1: import java.util.Random;
2: import java.security.SecureRandom;
3: ...
4: public Static int getRandomValue(int maxValue) {
//setSeed로 매번 변경되는 시드값을 설정 하거나, 기본값인 현재 시간 기반으로 매번
변경되는 시드값을 사용하도록 한다.
5: Random random = new Random();
6: return random.nextInt(maxValue);
7: }
8: public Static String getAuthKey() {
//보안결정을 위한 난수로는 예측이 거의 불가능하게 암호학적으로 보호된 SecureRandom을
사용한다.
9: try{
10: SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
11: MessageDigest digest = MessageDigest.getInstance("SHA-256");
12: secureRandom.setSeed(secureRandom.generateSeed(128));
13: String authKey = new String(digest.digest((secureRandom.nextLong() + "").getBytes()));
14: ...
15: } catch (NoSuchAlgorithmException e) {

ㅇ 내용

1. 랜덤값 받는 함수에서는 시드값을 랜덤으로 설정해놓고

2. 보안결정에 사용하는거는 CSPRNG사용함. 


ㅇ 분석

1: static int GenerateDigit()
2: {
//매번 변경되는 시드값을 사용하여 다른 난수값이 생성되나 보안결정을 위한
난수로는 안전하지 않다.
3: Random rng = new Random();
4: return rng.Next(10);
5: }

ㅇ 내용

1. 아니 근데 이게 보안결정용인지 단순 용도인지는 어떻게 구분함?

 

ㅇ 수정

1: static int GenerateDigitGood()
2: {
//보안결정을 위한 난수로는 예측이 거의 불가능하게 암호학적으로 보호된
SecureRandom을 사용한다.
3: byte[] b = new byte[4];
4: new
System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
5: return (b[0] & 0x7f) << 24 | b[1] << 16 | b[2] << 8 | b[3];
6: }

ㅇ 내용

1. 그냥 다 CSPRNG 쓰자


ㅇ 분석

1: void foo(){
2: int i;
3: for(i=0; i<20; i++)
// 프로그램을 여러 번 실행 했을 때 얻는 결과 값이 같다. 범위가 작기 때문에
암호화에 사용되기 힘들다.
4: printf("%d", rand());

ㅇ 내용

1. rand의 RNG 시퀀스가 동일하게 반복될 수 있으므로 사용하기 어려움(rand의 디폴트 시드는 1임)

 

ㅇ 수정

1: void foo(){
2: srandom(time(NULL));
3: int i;
4: for(i=0; i<20; i++)
5: printf("%ld", random());
6: }

ㅇ 내용

1. srandom을 이용해 시드값을 현재시간으로 선정해서 랜덤값에 대한 RNG가 가능함

 

 

끝.