서론: 비밀번호가 여전히 중요한 이유
1960년대 시분할 컴퓨터 시스템의 초기 시절부터 비밀번호는 사용자와 데이터 사이의 주요 문지기 역할을 해왔습니다. 1961년 MIT 연구원인 Fernando Corbató는 호환 시분할 시스템(CTSS)에 비밀번호를 도입했습니다. 이는 처음에는 보안 조치가 아니라 단순히 각 사용자에게 개인 파일 공간을 제공하기 위한 것이었습니다. 60년이 지난 지금도 비밀번호는 이메일 계정에서 은행 시스템에 이르기까지 모든 것을 보호하는 인터넷에서 가장 널리 사용되는 인증 메커니즘으로 남아 있습니다.
그러나 일반적인 사람들은 수십 개의 사이트에서 비밀번호를 재사용하고, Summer2024!와 같이 예측 가능한 패턴을 선택하며, 무엇이 진정으로 추측하기 어려운 비밀번호인지에 대한 직관이 부족합니다. 이 기사에서는 비밀번호 보안에 대해 기술적으로 심층 분석합니다. 엔트로피가 어떻게 계산되는지, 무작위성이 왜 중요한지, 최신 가이드라인은 무엇인지, 그리고 실제로 사용자를 보호하는 습관을 어떻게 기를 수 있는지 살펴봅니다.
엔트로피 이해: 예측 불가능성의 수학
정보 이론에서 **엔트로피(Entropy)**는 예측 불가능성을 측정합니다. 비밀번호의 경우, "공격자가 이 비밀번호를 알아내기 위해 평균적으로 몇 번의 추측이 필요한가?"라는 질문에 답합니다.
공식은 다음과 같습니다.
H = L × log₂(N)
여기서:
- H = 비트 단위의 엔트로피
- L = 비밀번호 길이(문자 수)
- N = 문자 세트의 크기(가능한 문자 풀)
문자 세트 크기
| 문자 세트 | 크기 (N) | 문자당 비트 수 |
|---|---|---|
| 소문자만 | 26 | 4.7 bits |
| 소문자 + 대문자 | 52 | 5.7 bits |
| 영문자 + 숫자 | 62 | 5.95 bits |
| 전체 인쇄 가능 ASCII | 94 | 6.55 bits |
| 확장 ASCII / 유니코드 | 128+ | 7+ bits |
엔트로피 예시
| 비밀번호 | L | N | 엔트로피 (H) |
|---|---|---|---|
password |
8 | 26 | 37.6 bits |
P@ssw0rd |
8 | 94 | 52.4 bits |
k9$mQzLw |
8 | 94 | 52.4 bits |
xK#7pL!qR2@v |
12 | 94 | 78.6 bits |
correct horse battery staple |
28 | 26 | 131.9 bits |
128비트 이상의 엔트로피를 가진 비밀번호는 현재 기술로 무차별 대입(brute-force) 공격이 계산상 불가능한 것으로 간주됩니다. 참고로, 해독이 불가능한 것으로 간주되는 256비트 AES 암호화는 2²⁵⁶개의 조합을 가진 키 공간에 해당합니다.
CSPRNG vs Math.random(): 무작위성 소스가 중요한 이유
모든 난수가 동일하게 생성되는 것은 아닙니다. 안전한 비밀번호 생성기와 안전하지 않은 생성기의 차이는 종종 **무작위성 소스(Source of Randomness)**에서 발생합니다.
Math.random() — 보안에 적합하지 않음
JavaScript의 내장 함수인 Math.random()은 **의사 난수 생성기(PRNG)**입니다. 속도가 빠르고 통계적으로 균일하지만 암호학적으로 안전하지 않습니다. 내부 상태가 출력에서 유추될 수 있으므로 충분한 값을 관찰한 공격자는 미래의 값을 예측할 수 있습니다.
// ❌ 안전하지 않음 — 비밀번호 생성에 사용하지 마세요
function insecurePassword(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
}
crypto.getRandomValues() — 올바른 도구
Web Cryptography API는 crypto.getRandomValues()를 제공합니다. 이는 운영 체제의 **암호학적으로 안전한 의사 난수 생성기(CSPRNG)**에서 엔트로피를 가져옵니다. Linux에서는 /dev/urandom, Windows에서는 CryptGenRandom이 이에 해당합니다. 이러한 소스는 하드웨어 이벤트(키 입력, 디스크 I/O, 네트워크 타이밍)에서 엔트로피를 수집하며 암호학적으로 강력한 것으로 간주됩니다.
// ✅ 안전함 — CSPRNG 사용
function generatePassword(length, charset) {
const array = new Uint32Array(length);
crypto.getRandomValues(array);
return Array.from(array, (val) => charset[val % charset.length]).join('');
}
const charset =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';
console.log(generatePassword(16, charset));
// 출력 예시: "aK7!xQz2#Lp9@Wm5"
모듈로 편향(Modulo Bias)에 대한 참고: 위 예시는
charset.length가 2³²을 나누어 떨어지지 않을 때 약간의 모듈로 편향이 발생할 수 있습니다. 프로덕션 코드에서는 거부 샘플링(Rejection Sampling)을 사용하여 이 편향을 완전히 제거해야 합니다.
문자 세트 및 복잡성
대부분의 비밀번호 생성기는 다음 옵션을 제공합니다.
- 소문자 (a–z): 26자
- 대문자 (A–Z): 26자
- 숫자 (0–9): 10자
- 기호 (!@#$%^&*…): 약 32자
이 네 가지를 모두 결합하면 94개의 인쇄 가능한 ASCII 문자 풀이 됩니다. 비밀번호에 문자가 하나씩 추가될 때마다 검색 공간은 94배로 늘어납니다. 94개 문자로 구성된 12자리 비밀번호는 94¹² ≈ 4.76 × 10²³개의 조합을 가지며, 이는 특수 하드웨어로도 천문학적인 숫자입니다.
길이가 복잡성보다 중요한 이유
많은 레거시 시스템은 "대문자, 숫자, 기호 각각 하나 이상 포함"을 요구하면서도 8자리 비밀번호를 허용했습니다. 94개 문자 세트의 8자리 비밀번호는 단 94⁸ ≈ 6 × 10¹⁵개의 조합만 가지며, 이는 현대적인 GPU 클러스터로 몇 시간 만에 해독될 수 있습니다. 반면 순수 소문자로 된 16자리 비밀번호(26¹⁶ ≈ 4.4 × 10²²)는 8자리 복잡한 비밀번호보다 훨씬 더 많은 엔트로피를 제공합니다.
NIST SP 800-63B: 현대적인 비밀번호 가이드라인
미국 국립표준기술연구소의 NIST 특별 간행물 800-63B(디지털 ID 가이드라인, 2017년 발표, 2024년 업데이트)는 비밀번호에 대한 많은 고정관념을 뒤집었습니다.
NIST의 현재 권장 사항
- 사용자 선택 비밀번호는 최소 8자, 기계 생성 비밀번호는 최소 15자로 설정합니다.
- 복잡성보다 길이를 선호합니다. 강제적인 복잡성 규칙(대문자+숫자+기호)은 보안을 의미 있게 강화하지 못하며 사용자에게 좌절감만 줍니다.
- 유출 목록에 대해 비밀번호를 확인합니다. 알려진 데이터 유출에서 발견된 비밀번호는 거부합니다(예: HaveIBeenPwned API 사용).
- 침해 증거가 없는 한 정기적인 강제 변경을 하지 않습니다. 강제 변경은
Summer2024→Autumn2024와 같이 예측 가능한 패턴으로 이어집니다. - 구성 규칙을 없앱니다. 특정 문자 유형을 요구하지 말고 공백을 포함한 모든 인쇄 가능한 문자를 허용합니다.
- 인증 시도를 속도 제한 및 차단하여 온라인 공격을 방지합니다.
이러한 가이드라인은 복잡성 제약 하에서 인간의 행동이 예측 가능하다는 현실을 반영합니다. 사람들은 기호 요구 사항을 맞추기 위해 끝에 !를 붙이고, 첫 글자만 대문자로 쓰며, 기억하기 쉬운 연도를 사용합니다.
비밀번호 관리자: 실질적인 해결책
대부분의 사람들이 할 수 있는 가장 큰 보안 개선 사항은 비밀번호 관리자를 도입하는 것입니다. 비밀번호 관리자는 다음과 같은 역할을 합니다.
- 모든 계정에 대해 강력하고 고유한 비밀번호를 생성합니다.
- 암호화된 저장소(일반적으로 AES-256)에 보관합니다.
- 브라우저와 앱에서 자격 증명을 자동으로 채웁니다.
- 재사용되거나 유출된 비밀번호에 대해 경고합니다.
인기 있는 비밀번호 관리자
| 도구 | 유형 | 주목할 만한 기능 |
|---|---|---|
| Bitwarden | 오픈 소스, 클라우드/셀프 호스팅 | 무료 등급 제공, 감사 완료 |
| 1Password | 상용, 클라우드 | 여행 모드, 가족 계획 |
| KeePass | 오픈 소스, 로컬 | 완전 오프라인, 플러그인 생태계 |
| KeePassXC | 오픈 소스, 로컬 | 크로스 플랫폼 KeePass 포크 |
| Dashlane | 상용, 클라우드 | 다크 웹 모니터링 |
비밀번호 관리자의 마스터 비밀번호는 길고 기억하기 쉬운 비밀번호 문구(Passphrase)여야 합니다. 이는 귀하가 기억해야 할 유일한 비밀번호입니다.
비밀번호 문구와 Diceware 방식
기억해야 하는 비밀번호(예: 비밀번호 관리자의 마스터 비밀번호)의 경우, 복잡한 짧은 문자열보다 **비밀번호 문구(Passphrases)**가 훨씬 우수합니다.
Diceware 작동 방식
1995년 Arnold Reinhold가 만든 Diceware 방식은 실제 주사위를 사용하여 진정한 무작위 단어 선택을 생성합니다.
- EFF Large Wordlist(7,776개 단어, 6진수 인덱스 11111–66666)를 준비합니다.
- 주사위 5개를 굴려 5자리 숫자를 얻습니다(예: 2-4-1-3-6 → 24136).
- 해당하는 단어를 찾습니다(예: "clump").
- 이 과정을 6~8번 반복하여 비밀번호 문구를 구성합니다.
7,776개 단어에서 추출된 6개 단어 Diceware 비밀번호 문구의 엔트로피는 다음과 같습니다.
H = 6 × log₂(7776) = 6 × 12.93 ≈ 77.6 bits
"correct horse battery staple"(XKCD #936으로 유명해짐)은 약 2,000개의 일반 어휘에서 4개 단어를 사용하며 약 44비트의 엔트로피를 제공합니다. 이는 예시로는 좋지만 실제로는 너무 짧습니다. 전체 Diceware 목록에서 6개 이상의 단어를 사용하는 것이 실질적인 권장 사항입니다.
일반적인 비밀번호 공격
공격 방식을 이해하면 방어 전략을 세우는 데 도움이 됩니다.
사전 공격(Dictionary Attacks)
공격자는 수백만 개에서 수십억 개의 항목이 포함된 단어 목록을 사용하며, 여기에 규칙 기반 변형(첫 글자 대문자화, 숫자 추가, a를 @로 대체 등)을 결합합니다. Hashcat과 같은 도구는 초당 수천 개의 변형 규칙을 적용할 수 있습니다. 사전 단어에 간단한 치환만 한 비밀번호는 모두 취약합니다.
무차별 대입 공격(Brute-Force Attacks)
순수 무차별 대입은 모든 조합을 시도합니다. 전용 GPU 리그(RTX 4090 8개)를 사용한 일반적인 해싱 알고리즘의 해독 속도는 다음과 같습니다.
| 해시 알고리즘 | 속도 (H/s) | 8자리(94개 문자 세트) 해독 시간 |
|---|---|---|
| MD5 | 약 200 GH/s | 약 8시간 |
| SHA-1 | 약 70 GH/s | 약 24시간 |
| bcrypt (cost 10) | 약 184 kH/s | 약 1,100년 |
| Argon2id | 약 1 kH/s | 약 200,000년 |
이 표는 서버 측에서 비밀번호 해싱을 제대로 하는 것이 왜 중요한지 보여줍니다. 또한 16자리 무작위 비밀번호가 MD5 해싱에 대해서도 얼마나 강력한지 보여줍니다.
레인보우 테이블(Rainbow Tables)
저장 공간과 속도를 맞바꾼 미리 계산된 해시-비밀번호 테이블입니다. 해싱 전에 각 비밀번호에 고유한 무작위 값을 추가하는 **솔팅(Salting)**으로 완벽하게 방어할 수 있습니다. bcrypt 및 Argon2와 같은 현대적인 알고리즘은 설계상 솔팅을 포함합니다.
자격 증명 스터핑(Credential Stuffing)
한 곳에서 유출된 사용자 이름/비밀번호 쌍을 사용하여 다른 서비스를 공격하는 것입니다. 방어책은 사이트마다 고유한 비밀번호를 사용하는 것이며, 여기서 비밀번호 관리자가 빛을 발합니다.
브라우저 기반 비밀번호 생성이 더 안전한 이유
본 도구는 JavaScript를 사용하여 브라우저에서 비밀번호를 완전히 생성합니다. 데이터가 귀하의 장치를 떠나지 않습니다. 이것이 중요한 이유는 다음과 같습니다.
- 서버 전송 없음 — 비밀번호가 네트워크 패킷을 거치지 않습니다.
- 서버 로그 없음 — 유출되거나 침해당할 대상 자체가 없습니다.
- 생성 시 제3자 의존성 없음 — 생성 중에 API 호출이나 외부 스크립트가 실행되지 않습니다.
- 재현 가능 — 소스 코드를 검사하여 로직을 확인할 수 있습니다.
이를 서버 측 생성기와 비교해 보세요. HTTPS를 사용하더라도 생성된 비밀번호는 서버 메모리에 존재하고 액세스 로그에 나타날 수 있으며 전적으로 운영자의 신뢰성에 의존하게 됩니다.
관련 Web API는 간단합니다.
// 브라우저의 CSPRNG — 모든 현대적인 브라우저에서 사용 가능
const buffer = new Uint8Array(32);
self.crypto.getRandomValues(buffer);
// buffer는 이제 32바이트의 암호학적으로 무작위인 값을 포함함
비밀번호 저장: bcrypt, scrypt 및 Argon2
서비스가 비밀번호를 저장할 때 절대 평문이나 복호화 가능한 암호화 형식으로 저장해서는 안 됩니다. 올바른 방법은 **비밀번호 해시 함수(PHF)**를 사용하는 것입니다. 이는 이 목적을 위해 특별히 설계된 느린 일방향 함수입니다.
bcrypt
1999년 Niels Provos와 David Mazières가 설계한 bcrypt는 하드웨어가 빨라짐에 따라 증가시킬 수 있는 **비용 계수(Work Factor)**를 포함합니다. 비용 12는 Blowfish 키 설정의 2¹² = 4,096번 반복을 의미합니다. 오늘날 표준이며 널리 지원됩니다.
scrypt
2009년 Colin Percival이 설계했습니다. 메모리 하드(Memory-hard) 방식으로 설계되어 CPU 시간 외에도 대량의 RAM을 요구하므로 GPU/ASIC 공격 비용이 많이 듭니다. 매개변수: N(CPU/메모리 비용), r(블록 크기), p(병렬화).
Argon2
2015년 비밀번호 해싱 대회(PHC)의 우승자입니다. 세 가지 변형이 있습니다.
- Argon2d: GPU 저항성이 있지만 부채널 공격에 취약함
- Argon2i: 부채널 공격 저항성이 있지만 GPU 저항성이 낮음
- Argon2id: 하이브리드 방식 — 권장되는 기본값
m=65536(64MB 메모리), t=3(3번 반복), p=4(4개 스레드) 설정의 Argon2id는 현재 새로운 애플리케이션의 골드 표준입니다.
2단계 인증: 필수적인 보완책
완벽한 비밀번호라도 피싱, 키로거 또는 데이터 유출을 통해 도난당할 수 있습니다. **2단계 인증(2FA)**은 도난당한 비밀번호만으로는 충분하지 않도록 보장합니다.
2FA 방법(약한 것부터 강한 것 순)
| 방법 | 메커니즘 | 공격 저항성 |
|---|---|---|
| SMS OTP | 문자로 전송되는 코드 | 피싱, SIM 스왑에 취약 |
| TOTP (Google Authenticator) | 시간 기반 6자리 코드 (RFC 6238) | 실시간 피싱에 취약 |
| 푸시 알림 | 휴대폰에서 승인/거부 | 피싱(MFA 피로 공격)에 취약 |
| 하드웨어 키 (FIDO2/WebAuthn) | YubiKey, Passkey | 피싱 저항성 있음 |
| Passkeys (패스키) | 장치 바인딩된 암호화 키 | 가장 강력함. 비밀번호 대체 |
FIDO2/WebAuthn 하드웨어 키와 패스키는 암호화된 챌린지-응답이 정확한 도메인에 바인딩되므로 설계상 피싱에 저항성이 있습니다. 가짜 사이트는 자격 증명을 재사용할 수 없습니다.
모범 사례 요약
- 비밀번호 관리자 사용 — 모든 계정에 대해 고유한 비밀번호를 생성하고 저장하세요.
- 중요 계정은 최소 16자, 그 외에는 최소 12자를 사용하세요.
- 2FA 활성화 — 가급적 FIDO2/WebAuthn 또는 TOTP를 사용하고 SMS는 피하세요.
- 비밀번호 재사용 금지 — 한 사이트의 유출이 다른 사이트의 침해로 이어지지 않게 하세요.
- HaveIBeenPwned 확인 — 이메일 주소와 비밀번호가 알려진 유출에 포함되지 않았는지 확인하세요.
- 마스터 비밀번호에 비밀번호 문구 사용 — Diceware 단어 6개 이상. 기억하기 쉽고 매우 강력합니다.
- 복잡성 연극에 속지 마세요 —
P@ssw0rd123은xK8mLq2vZnRj보다 훨씬 취약합니다. - 길이를 우선시하세요 — 20자 무작위 소문자(94비트)가 12자 복잡한 문자(78비트)보다 낫습니다.
- 유출된 비밀번호 즉시 업데이트 — 임의의 일정이 아니라 침해되었을 때만 변경하세요.
- 유출 모니터링 도구 사용 — Firefox Monitor나 1Password Watchtower와 같은 서비스를 사용하여 계정을 지속적으로 모니터링하세요.
자주 묻는 질문
Q: 비밀번호는 얼마나 길어야 하나요? 대부분의 계정에서 전체 94개 문자 세트 중 무작위로 선택된 16자는 약 105비트의 엔트로피를 제공하며 충분합니다. 은행, 이메일, 비밀번호 관리자와 같은 중요 계정에는 20자 이상 또는 6개 단어의 비밀번호 문구를 사용하세요.
Q: 온라인 비밀번호 생성기를 사용하는 것이 안전한가요? 비밀번호 생성 프로세스가 서버 통신 없이 전적으로 클라이언트(브라우저)에서 이루어지는 경우에만 안전합니다. 본 도구는 이 요구 사항을 충족합니다. 비밀번호를 생성할 때 브라우저의 네트워크 탭에 요청이 발생하지 않는지 확인하여 검증할 수 있습니다.
Q: 기호를 포함해야 하나요? 기호는 문자당 엔트로피를 증가시키므로(소문자 전용 5.17비트 대비 6.55비트) 사이트에서 허용한다면 포함하는 것이 좋습니다. 하지만 기호가 없는 긴 비밀번호도 기호가 있는 짧은 비밀번호와 동일한 엔트로피를 가질 수 있습니다.
Q: 양자 컴퓨터가 제 비밀번호를 해독할 수 있나요? 그로버 알고리즘은 양자 컴퓨터에 무차별 대입 공격에 대한 이차적 속도 향상을 제공하여 보안 비트를 실질적으로 절반으로 줄입니다. 256비트 키는 128비트 보안 수준이 됩니다. 비밀번호의 경우 장기적인 양자 저항성을 위해 256비트 엔트로피(94개 문자 세트 중 40자 상당)가 필요합니다. 현재 대부분의 위협에 대해 128비트 엔트로피(20자 무작위 문자)로 충분합니다.
Q: ILovePizza2024와 같이 기억하기 쉬운 비밀번호의 문제는 무엇인가요?
사전 공격에 취약합니다. Hashcat의 규칙 엔진은 leetspeak, 대문자화, 숫자 추가 등을 통해 일반적인 문구의 수백만 가지 변형을 쉽게 생성합니다. 겉보기에 개인적인 비밀번호도 공격자가 통계적으로 모델링할 수 있는 패턴을 따릅니다.
Q: 이 도구는 어떻게 무작위성을 생성하나요?
운영 체제의 CSPRNG를 기반으로 하는 Web Cryptography API의 crypto.getRandomValues()를 사용합니다. 이는 TLS/SSL, SSH 키 생성 및 브라우저의 기타 암호화 작업에 사용되는 것과 동일한 무작위성 소스입니다.