비밀번호 생성기
사용자 정의 가능한 길이와 문자 집합으로 강력한 임의 비밀번호 생성
비밀번호 생성기란?
비밀번호 생성기는 사용자 계정, API 키, 데이터베이스 자격 증명, 암호화 비밀 키에 사용할 무작위 문자열을 생성합니다. 사람이 직접 선택한 비밀번호와 달리, 생성된 비밀번호는 가능한 모든 문자 조합 공간에서 추출되므로 사전 공격 및 패턴 기반 추측에 강합니다. 이 도구는 브라우저에 내장된 암호학적 난수 생성기를 사용하여 강력한 무작위 비밀번호를 온라인으로 생성합니다.
비밀번호 강도는 두 가지 요소에 따라 결정됩니다: 길이와 문자 다양성. 대문자, 소문자, 숫자, 특수문자를 모두 사용하는 20자 비밀번호는 약 131비트의 엔트로피를 가집니다. 이 수준에서는 초당 1조 번의 추측을 시도하는 무차별 대입 공격으로도 모든 조합을 시도하는 데 우주의 나이보다 더 긴 시간이 필요합니다. 계산 방식은 간단합니다: 엔트로피 = 길이 × log2(문자 집합 크기).
NIST SP 800-63B와 같은 표준은 최소 8자 이상의 비밀번호를 권장하며 검증자가 상한선을 강제하지 않도록 하고, '정확히 특수문자 하나 포함'과 같은 강제 조합 규칙보다는 긴 패스프레이즈를 권장합니다. 기계 간 자격 증명 및 서비스 계정의 경우, 대부분의 보안 프레임워크 및 규정 준수 체계에서 전체 문자 집합에서 추출한 20자 이상의 무작위 문자가 기본 기준으로 인정됩니다.
왜 비밀번호 생성기를 사용해야 할까요?
사람은 좋은 난수 생성기가 아닙니다. 비밀번호를 재사용하거나, 사전에 있는 단어를 선택하거나, 예측 가능한 패턴으로 문자를 치환하고(@를 a로, 3을 e로), 짧은 문자열을 기본값으로 사용합니다. 비밀번호 생성기는 이 과정에서 인간의 편향을 제거합니다.
비밀번호 생성기 활용 사례
비밀번호 엔트로피 참고표
엔트로피는 비밀번호의 예측 불가능성을 측정합니다. log2(문자_집합_크기 ^ 길이)로 계산됩니다. 엔트로피가 높을수록 공격자가 탐색해야 할 가능한 조합이 많아집니다. NIST와 OWASP는 높은 보안 수준의 애플리케이션에 최소 80비트의 엔트로피를 권장합니다.
| 길이 | 문자 집합 | 엔트로피 | 무차별 대입 소요 시간 |
|---|---|---|---|
| 8 | lower + digits | ~41 bits | Minutes to hours |
| 12 | lower + upper + digits | ~71 bits | Centuries (offline) |
| 16 | all character sets | ~105 bits | Beyond brute-force |
| 20 | all character sets | ~131 bits | Beyond brute-force |
| 32 | all character sets | ~210 bits | Beyond brute-force |
| 64 | all character sets | ~419 bits | Beyond brute-force |
해독 시간은 초당 1조 번 추측(현대 GPU를 사용한 오프라인 공격) 기준입니다. 속도 제한이 있는 온라인 공격은 수십억 배 더 느립니다.
비밀번호 생성 시 CSPRNG vs Math.random()
무작위성의 출처는 비밀번호 길이만큼 중요합니다. 예측 가능한 난수 생성기로 생성된 비밀번호는 알고리즘과 시드 상태를 아는 공격자가 재구성할 수 있습니다. 이 도구는 모든 현대 브라우저에 내장된 암호학적으로 안전한 유사난수 생성기(CSPRNG)인 crypto.getRandomValues()를 사용합니다.
코드 예시
다양한 언어로 프로그래밍 방식으로 비밀번호를 생성합니다. 아래의 모든 예시는 Math.random() 또는 이와 동등한 취약한 PRNG가 아닌 암호학적으로 안전한 무작위 소스를 사용합니다.
// Generate a random password in the browser or Node.js 19+
function generatePassword(length = 20) {
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*'
const values = new Uint32Array(length)
crypto.getRandomValues(values)
return Array.from(values, v => charset[v % charset.length]).join('')
}
console.log(generatePassword()) // → "kR7!mZp$Xw2&nLq9@Yf3"
console.log(generatePassword(32)) // → "Hd4%tNx!Qw8#mKp2Rv6&Zj0*Ls3Yb7@"import secrets
import string
def generate_password(length: int = 20) -> str:
"""Generate a cryptographically secure random password."""
alphabet = string.ascii_letters + string.digits + string.punctuation
return ''.join(secrets.choice(alphabet) for _ in range(length))
# Single password
print(generate_password()) # → "kR7!mZp$Xw2&nLq9@Yf3"
# Batch of 5 passwords
for _ in range(5):
print(generate_password(24))
# Ensure at least one char from each category
def generate_strong(length: int = 20) -> str:
required = [
secrets.choice(string.ascii_uppercase),
secrets.choice(string.ascii_lowercase),
secrets.choice(string.digits),
secrets.choice(string.punctuation),
]
remaining = length - len(required)
alphabet = string.ascii_letters + string.digits + string.punctuation
all_chars = required + [secrets.choice(alphabet) for _ in range(remaining)]
secrets.SystemRandom().shuffle(all_chars)
return ''.join(all_chars)# OpenSSL — generate 32 random bytes, base64-encode openssl rand -base64 32 # → "x7Kp2mNqR4wZ8vLs1Yb0Hd6tFj3Xc9Ga5eUi+Wo=" # /dev/urandom with tr — alphanumeric + symbols, 20 chars tr -dc 'A-Za-z0-9!@#$%^&*' < /dev/urandom | head -c 20; echo # → "kR7!mZp$Xw2&nLq9@Yf3" # pwgen (install: apt install pwgen / brew install pwgen) pwgen -sy 20 5 # Generates 5 passwords, 20 chars each, with symbols
package main
import (
"crypto/rand"
"fmt"
"math/big"
)
func generatePassword(length int) (string, error) {
charset := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()"
result := make([]byte, length)
for i := range result {
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
if err != nil {
return "", err
}
result[i] = charset[idx.Int64()]
}
return string(result), nil
}
func main() {
pwd, _ := generatePassword(20)
fmt.Println(pwd) // → "kR7!mZp$Xw2&nLq9@Yf3"
}