예측 가능한 난수를 사용하면 시스템에 보안약점을 유발한다.
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
아래는 소프트웨어 개발보안 가이드에서 "적절하지 않은 난수값 사용"부분만 저장한 부분이다.
전체 가이드는 KISA의 보안취약점 및 침해사고 대응 페이지에서 가이드를 제공하고 있다.
https://www.kisa.or.kr/2060204/form?postSeq=5&lang_type=KO&page=1#fnPostAttachDownload
'보안 > 시큐어코딩' 카테고리의 다른 글
[시큐어코딩] 경로 조작 및 자원삽입_개념 및 보안대책 (0) | 2024.03.19 |
---|---|
[시큐어코딩] 부적절한 자원 해제_개념 및 해결방법 (0) | 2024.02.04 |