홈으로 돌아가기

Python 비밀번호 생성: random 대신 secrets

이 글은 Mersenne Twister의 결정성 때문에 random 모듈이 비밀번호 생성에 부적합한 이유를 분석합니다. secrets와의 비교, 엔트로피 분석 및 실패한 시드 공격 시도. 안전한 코드 추천.

Python에서 random이 비밀번호에 위험한 이유: secrets 실전
Advertisement 728x90

파이썬에서 암호화적으로 안전한 비밀번호 생성: random 대 secrets

엔트로피는 비밀번호의 품질을 문자열 자체의 속성이 아닌 생성 과정의 특성으로 정의합니다. 공식 H = L × log₂(N)은 길이 L 동안 크기 N의 알파벳에서 각 문자가 독립적이고 동일한 확률로 선택된다고 가정합니다. 소문자 8자(N=26)의 경우: 약 37.6비트. 전체 ASCII(N=94)의 경우: 약 79비트.

흔한 오해: 비밀번호의 표면적 무작위성이 강도를 보장한다는 것입니다. f9A$kL2pQzX1과 같은 두 비밀번호는 형식적 엔트로피가 동일할 수 있지만 사용된 생성기에 따라 실제 보안성은 크게 다를 수 있습니다.

random 모듈: 결정론적 PRNG

random 모듈은 내부 상태가 약 19937비트인 메르센 트위스터(MT19937)를 구현합니다. 각 값은 이전 값에 기반해 결정론적입니다. 기본 시드는 os.urandom()에서 가져오지만, PRNG의 고유 속성은 그대로 유지됩니다:

Google AdInline article slot
  • 결정론성: 하나의 시드는 하나의 예측 가능한 시퀀스를 생성합니다.
  • 복구 가능성: 상태는 약 624개의 원시 32비트 출력에서 재구성될 수 있습니다.
  • 암호화적 보안 부재: 상태 복구 공격에 취약합니다.

일반적인 코드 예시:

import random
import string

alphabet = string.ascii_letters + string.digits
def generate_password(length=12):
    return ''.join(random.choice(alphabet) for _ in range(length))

비밀번호는 PRNG 시퀀스의 일부입니다. 상태가 알려지면 시퀀스는 예측 가능합니다.

secrets 모듈: 시스템 엔트로피에 대한 직접 접근

Secrets는 각 호출에 대해 os.urandom()을 직접 사용합니다:

Google AdInline article slot
  • secrets.randbelow(n) → os.urandom().
  • random.SystemRandom() → os.urandom().

고정된 상태나 결정론성이 없습니다. 각 바이트는 하드웨어 소스(인터럽트, 네트워크 노이즈, 마우스 이동)에서 추출됩니다.

차이는 결정적입니다: random은 보안의 환상을 만들지만, secrets는 실제 보안을 제공합니다.

실제 공격: 시드 및 상태 복구

GigaChat에서 생성된 5개의 연속 비밀번호 실험(길이 12, Python 3.9.18, 시간 2026년 3월 24일 11:11):

Google AdInline article slot

가설: 시드 = 서버 타임스탬프 ±1시간.

from datetime import datetime, timedelta

base_time = datetime(2026, 3, 24, 11, 11, 0)
window_seconds = 3600
for offset in range(-window_seconds, window_seconds):
    t = base_time + timedelta(seconds=offset)
    seed = int(t.timestamp())
    # random.seed(seed) 초기화 및 검증

결과: 일치 없음. 시드에 os.urandom()을 사용하면 시간 기반 무차별 대입 공격을 무력화합니다.

MT19937에 대한 실제 공격은 624개의 원시 값이 필요합니다. 비밀번호는 왜곡된 출력(선택 → 문자)으로, 5개 비밀번호에 대해 총 60개의 선택만 있습니다. 복구는 불가능합니다.

공격 경계:

  • 원시 PRNG 출력 필요.
  • 예측 가능한 시드(시간, PID).
  • 동일 생성기 반복 사용.
  • 알려진 알고리즘.

핵심 요점

  • 엔트로피는 결과가 아닌 과정의 속성: 항상 생성기를 확인하세요.
  • random(메르센 트위스터)은 충분한 데이터가 주어지면 상태 복구에 취약합니다.
  • secrets/os.urandom은 비밀번호, 키, 토큰에 대한 유일한 선택입니다.
  • random의 무작위성 환상은 프로덕션에서 위험합니다.
  • PRNG 공격은 시드 오류나 상태 유출 시에만 현실적입니다.

중급/시니어 개발자를 위한 권장사항

코드에서 구현:

import secrets
import string

alphabet = string.ascii_letters + string.digits + string.punctuation
def secure_password(length=16):
    return ''.join(secrets.choice(alphabet) for _ in range(length))

피하세요:

  • 인증에 random 사용.
  • 고정된 시드.
  • 생성기 재사용.

테스트: 10⁶개의 비밀번호 생성, 균일 분포 확인(카이제곱 검정).

— Editorial Team

Advertisement 728x90

다음 읽기