현대 컴퓨팅 시스템에서 난수는 비밀번호 생성부터 간단한 복권 게임에 이르기까지 어디에나 존재합니다. 그러나 암호학 분야에서는 난수의 보안이 단순히 낮은 확률로 “복권에 당첨되는” 문제가 아니라, 시스템이 공격을 견딜 수 있는지를 결정하는 중요한 요소입니다.
만약 난수 생성기가 안전하지 않다면, 다음과 같은 문제가 발생할 수 있습니다:
- 암호화 키 예측으로 인해 시스템 통신이 쉽게 깨질 수 있음;
- 세션 토큰 위조로 인해 사용자 계정이 불법적으로 탈취될 수 있음;
- 서명 알고리즘 실패로 인해 디지털 서명이 법적 효력을 잃을 수 있음.
따라서 암호화 응용 프로그램에서는 “겉보기에는 무작위”인 숫자 시퀀스뿐만 아니라 예측 불가능하고 안전한 난수가 필요합니다. 여기서 **암호학적으로 안전한 의사 난수 생성기 (CSPRNG)**가 중요한 역할을 합니다.
CSPRNG란 무엇인가?
CSPRNG는 암호화 응용 프로그램을 위해 설계된 특별한 형태의 의사 난수 생성기 (PRNG)로, 더 높은 보안 기준을 가지고 있습니다.
CSPRNG의 핵심 특성은 다음과 같습니다:
- 예측 저항성: 공격자가 알려진 부분을 기반으로 난수 시퀀스의 다른 부분을 예측할 수 없습니다.
- 역방향 추적 불가능성: 공격자가 내부 상태를 탈취하더라도 이전에 생성된 난수 시퀀스를 복원할 수 없습니다.
CSPRNG의 중요성을 이해하기 위해 다른 난수 생성 방법과 비교해 봅시다.
난수 생성기의 비교
-
진정한 난수 생성기 (TRNG)
TRNG는 물리적 현상(예: 하드웨어 노이즈, 전자기 간섭)을 기반으로 완전히 무작위 값을 생성하지만, 속도가 느리고 하드웨어에 크게 의존합니다.
-
의사 난수 생성기 (PRNG)
PRNG는 알고리즘과 시드를 기반으로 난수를 생성하며, 효율적이지만 예측 가능하고 암호화 시나리오에는 적합하지 않습니다.
-
CSPRNG
CSPRNG는 암호학적 원칙에 기반하여 설계되어 PRNG의 보안 취약점을 해결하고, 생성된 난수 시퀀스가 예측 불가능하도록 보장합니다.
예를 들어, PRNG는 자동으로 시를 생성하는 프로그램과 같아서 시작 문장(시드)만 알면 공격자가 이후 문장을 예측할 수 있습니다. 반면, CSPRNG는 시의 소스 코드를 잠그는 프로그램과 같아서 외부에서 다음 문장을 예측하는 것이 거의 불가능합니다.
CSPRNG의 원리와 알고리즘
일반적인 CSPRNG 알고리즘
-
HMAC 결정적 난수 비트 생성기 (HMAC-DRBG)
HMAC (해시 기반 메시지 인증 코드) 알고리즘에 기반한 CSPRNG로, 키와 의사 난수 값을 사용하여 내부 상태를 업데이트합니다. HMAC-DRBG는 이중 상태 관리(키
K
와 의사 난수 값V
)를 통해 예측 불가능성과 역방향 추적 불가능성을 향상시킵니다. -
SHA-256 기반 PRNG
SHA-256 해시 알고리즘에 기반한 난수 생성기로, 추가 키가 필요하지 않고 상태 값에만 의존합니다. 업데이트 메커니즘이 간단하지만, HMAC-DRBG만큼 공격에 대한 저항력이 좋지 않습니다.
-
카운터 모드 결정적 난수 비트 생성기 (CTR-DRBG)
블록 암호 모드(예: AES)에 기반한 난수 생성기로, 카운터 (CTR) 모드를 사용하여 난수를 생성합니다. 고성능 및 높은 보안 요구 시나리오에 적합하며, 블록 암호 알고리즘의 보안에 의존하고 하드웨어 구현에 적합합니다.
난수 시드 생성
- 운영 체제의 진정한 난수 생성기 (TRNG)를 호출하여 높은 엔트로피 시드를 얻습니다.
- 하드웨어 보안 모듈 (HSM) 또는 시스템 엔트로피 소스(예:
/dev/urandom
)를 사용하여 시드를 생성하여 CSPRNG의 초기 상태의 무작위성을 보장합니다.
CSPRNG를 어떻게 사용할까?
앞서 설명한 원리에서 알 수 있듯이, CSPRNG의 효과를 보장하기 위해서는 진정한 난수 시드로 시작해야 합니다. 초기 시드가 진정한 무작위성을 보장할 수 없다면, 이론적으로 CSPRNG 알고리즘에만 의존해서는 난수의 진정한 암호학적 보안을 보장하기 어렵습니다.
사실, 현대 운영 체제는 이미 이 문제를 고려하여 시스템 수준의 진정한 난수 인터페이스를 제공하며, 대부분 하드웨어 노이즈, 키보드 및 마우스 이벤트와 같은 기본 엔트로피 소스를 기반으로 고품질의 난수를 생성합니다.
예를 들어, Linux와 macOS의 /dev/random
및 /dev/urandom
인터페이스, Windows의 CryptGenRandom
또는 BCryptGenRandom
인터페이스가 있습니다.
일반적인 프로그래밍 언어도 시스템 수준의 진정한 난수 인터페이스를 캡슐화하여 제공합니다:
- Node.js의
crypto.randomBytes()
메서드; - Java의
SecureRandom
클래스; - Python의
os.urandom()
인터페이스와secrets
모듈 등.
시스템 수준의 진정한 난수를 생성할 수 있는데, 왜 여전히 CSPRNG 알고리즘이 필요한지 궁금할 수 있습니다.
앞서 언급했듯이, 운영 체제의 엔트로피 풀 용량은 제한되어 있어 진정한 난수를 생성하는 속도가 제한적입니다. 짧은 시간 내에 많은 수의 난수가 필요한 시나리오에서는 시스템 수준의 난수가 요구를 충족할 수 없습니다. CSPRNG는 시스템 수준의 진정한 난수를 보완하여 암호학적으로 안전한 난수를 더 빠르게 생성하고, 더 높은 커스터마이징과 더 많은 난수 비트를 제공합니다.
CSPRNG를 사용할 때 다음 사항에 유의해야 합니다:
- 일반적인 의사 난수 시드를 사용할 경우, 공격자가 난수 시퀀스를 예측하여 CSPRNG 출력이 안전하지 않게 될 수 있습니다. 앞서 언급한 시스템 수준의 인터페이스를 사용하여 진정한 난수를 시드로 생성할 수 있습니다.
- CSPRNG의 내부 상태가 유출되면 생성된 난수를 예측할 수 있습니다. 이러한 상황을 피하기 위해 CSPRNG의 상태나 시드를 주기적으로 업데이트할 수 있습니다.
응용 예시
키 생성
암호화된 통신에서 키의 보안은 시스템의 보안을 직접 결정합니다. 키가 예측 가능하다면, 공격자가 무차별 대입 공격이나 키 생성 패턴 분석을 통해 키를 획득하여 통신 내용을 해독할 수 있습니다. 따라서 키 생성에는 매우 높은 예측 불가능성을 가진 난수가 필요하며, CSPRNG의 특성(예측 저항성과 역방향 추적 불가능성)은 생성된 키가 예측하기 어렵도록 보장합니다.
세션 토큰
웹 애플리케이션에서 세션 토큰은 사용자 세션(예: 로그인 상태)을 식별하는 데 사용됩니다. 토큰이 공격자에 의해 추측되거나 위조되면 세션 하이재킹(Session Hijacking)이 발생하여 사용자를 가장하여 작업을 수행할 수 있습니다.
예를 들어, CSRF와 PKCE에서는 세션이나 프로세스 공격을 방지하기 위해 무작위 state
또는 code
를 사용합니다.
또한, 해시 알고리즘을 사용하여 DB에 저장할 비밀번호를 처리할 때, 단순한 해시 알고리즘만 사용할 경우 레인보우 테이블 공격에 저항하기 어렵습니다. 이러한 경우, 해시 알고리즘을 사용할 때 Salt라는 무작위 변수를 추가하여 동일한 비밀번호가 다른 해시 값을 생성하도록 합니다.