본문 바로가기
보안/시큐어코딩

[시큐어코딩] 적절하지 않은 난수값 사용, random() 취약점

by noootella 2023. 11. 4.
반응형

 

예측 가능한 난수를 사용하면 시스템에 보안약점을 유발한다. 

random()함수가 그 대표적인 예로, 시큐어 코딩이 필요하다.

 

 

행정안전부에서 SW개발보안 가이드 47개 항목 중 보안 기능의 24번째 항목

"적절하지 않은 난수값 사용"에 해당하는 부분이다.

소프트웨어 개발보안 가이드(행정안전부, 한국인터넷진흥원)

 

오늘은 난수값의 정확한 시큐어코딩 방법에 대해 공부하고자 한다.

 


 

난수란?

 어떤 주기로 반복되지 않고, 특정한 규칙 없이 무작위로 나열돼 있는 수

 

쉽게 말해서 예측 불가능한 수라고 생각하면 된다.

 

난수를 사용하지 않고 예측 가능한 수를 사용하면, 공격자는 SW에서 생성되는 다음 숫자를 예상하여 시스템을 공격하는 것이 가능하다.

 

예를 들어 Java의 경우 랜덤 난수를 발생시킬 때 보통 Math.random()을 많이 사용하는데,

해당 메소드의 사용은 시드값을 설정할 수 없고 사용하는 알고리즘이 밝혀지면 취약해질 수 있다.

 


 

보안대책

 

  • Java : 예측이 거의 불가능하게 암호학적으로 보호된 java.security.SecureRandom 클래스를 사용하는 것이 안전
  • C : rand()함수 사용 시 매번 변경되는 기본 시드(Seed)값이 없으므로, srand()로 매번 변경되는 현재시간 기반 등으로 시드(Seed)값을 설정하여하 함

 

요약하자면

Java java.security.SecureRandom 클래스 사용
C
srand() 사용

 

 

만약 JavaScript를 사용하고 있는데 random()함수가 걸렸다. 라고 한다면 해당 언어는 Client Side언어이기 때문에

난수를 생성하고자 한다면 Server Side에서 작성하는 것을 권고드릴 것 같다.

 

 

하지만 부득이하게 JavaScript를 써야 한다면,

JavaScript window.crypto.getRandomValues 사용

 

 

 

코드예제

 

아래는 SW개발보안가이드에 나와있는 코드 예제이다.


JAVA 안전하지 않은 코드

import java.util.Random;

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

 

JAVA 안전한 코드의 예

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

C# 안전하지 않은 코드

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

 

C# 안전한 코드의 예

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

 

 

C 안전하지 않은 코드

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

 

C 안전한 코드의 예

void foo() {
 srandom(time(NULL));
 int i;
 for(i=0; i<20; i++)
 printf("%ld", random());
}

 

 

 

자세한 사항은 CWE사이트에서도 확인 가능하다

https://cwe.mitre.org/data/definitions/330.html

OWASP 사이트에서도 해당 사항에 대한 설명이 존재한다.

https://owasp.org/www-community/vulnerabilities/Insecure_Randomness

 

아래는 소프트웨어 개발보안 가이드에서 "적절하지 않은 난수값 사용"부분만 저장한 부분이다.

SW개발보안가이드-적절하지 않은 난수값 사용.pdf
0.31MB

 

 

전체 가이드는 KISA의 보안취약점 및 침해사고 대응 페이지에서 가이드를 제공하고 있다.

https://www.kisa.or.kr/2060204/form?postSeq=5&lang_type=KO&page=1#fnPostAttachDownload

반응형