안전한 비밀번호 관리 완벽 가이드
개인정보 유출과 해킹 사고가 일상화된 시대, 강력한 비밀번호는 디지털 보안의 첫 번째 방어선입니다. 올바른 비밀번호 생성과 관리 전략으로 개인과 기업의 디지털 자산을 보호할 수 있습니다.
1. 비밀번호 보안의 중요성
현재 위협 환경
사이버 공격 통계 (2024년 기준)
const cybersecurityStats = {
// 전 세계 데이터 유출 현황
globalBreaches: {
annualIncidents: 3200,
exposedRecords: '45억 건',
averageCostPerBreach: '$4.45M',
identityTheftCases: '143만 건'
},
// 비밀번호 관련 보안 사고
passwordAttacks: {
credentialStuffing: '61% 증가', // 기존 유출된 계정으로 다른 서비스 공격
bruteForceAttacks: '28% 증가',
passwordSpraying: '34% 증가', // 여러 계정에 흔한 비밀번호 시도
phishingSuccess: '12% 성공률'
},
// 일반적인 비밀번호 취약점
commonVulnerabilities: {
reusedPasswords: '65%의 사용자', // 같은 비밀번호 재사용
weakPasswords: '23%의 계정', // 취약한 비밀번호 사용
noMFA: '78%의 개인 계정', // 2단계 인증 미사용
storedInBrowser: '51%의 사용자' // 브라우저에 비밀번호 저장
}
};
해킹 방법과 대응
const attackMethods = {
// 1. 사전 공격 (Dictionary Attack)
dictionaryAttack: {
description: '일반적인 비밀번호 목록을 사용한 공격',
commonTargets: ['password', '123456', 'admin', 'qwerty'],
prevention: '복잡하고 예측 불가능한 비밀번호 사용',
timeToBreak: '몇 초 ~ 몇 분'
},
// 2. 무차별 대입 공격 (Brute Force)
bruteForceAttack: {
description: '모든 가능한 조합을 시도하는 공격',
computingPower: '1초당 10억 회 시도 가능 (GPU 사용 시)',
prevention: '길고 복잡한 비밀번호 + 계정 잠금 정책',
timeToBreak: {
'8자리 숫자': '2.5시간',
'8자리 영문+숫자': '22일',
'12자리 영문+숫자+특수문자': '34,000년'
}
},
// 3. 레인보우 테이블 공격
rainbowTableAttack: {
description: '미리 계산된 해시 테이블을 이용한 공격',
target: '해시화된 비밀번호 데이터베이스',
prevention: '솔트(Salt) 사용 + 강력한 해시 함수',
effectiveness: '일반적인 비밀번호 99% 크랙 가능'
},
// 4. 사회공학적 공격
socialEngineering: {
description: '개인 정보를 이용한 비밀번호 추측',
commonSources: ['생년월일', '이름', '전화번호', '애완동물 이름'],
prevention: '개인 정보와 무관한 무작위 비밀번호',
successRate: '개인 정보 기반 비밀번호의 43% 추측 가능'
}
};
비밀번호 복잡성 평가
강도 측정 알고리즘
class PasswordStrengthAnalyzer {
constructor() {
this.patterns = {
lowercase: /[a-z]/,
uppercase: /[A-Z]/,
numbers: /[0-9]/,
specialChars: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/,
repeating: /(.)\1{2,}/,
sequential: /(abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz|123|234|345|456|567|678|789)/i,
commonWords: ['password', 'admin', 'user', 'login', '123456', 'qwerty']
};
}
analyzeStrength(password) {
const analysis = {
score: 0,
length: password.length,
hasLowercase: this.patterns.lowercase.test(password),
hasUppercase: this.patterns.uppercase.test(password),
hasNumbers: this.patterns.numbers.test(password),
hasSpecialChars: this.patterns.specialChars.test(password),
hasRepeating: this.patterns.repeating.test(password),
hasSequential: this.patterns.sequential.test(password),
isCommonWord: this.isCommonPassword(password),
entropy: this.calculateEntropy(password),
timeToBreak: null,
improvements: [],
strength: 'very_weak'
};
// 점수 계산
analysis.score += this.calculateLengthScore(password.length);
analysis.score += this.calculateComplexityScore(analysis);
analysis.score += this.calculatePatternPenalty(analysis);
// 강도 분류
analysis.strength = this.classifyStrength(analysis.score);
analysis.timeToBreak = this.estimateBreakTime(analysis);
analysis.improvements = this.generateImprovements(analysis);
return analysis;
}
calculateLengthScore(length) {
if (length < 6) return 0;
if (length < 8) return 10;
if (length < 12) return 25;
if (length < 16) return 35;
return 45;
}
calculateComplexityScore(analysis) {
let score = 0;
if (analysis.hasLowercase) score += 5;
if (analysis.hasUppercase) score += 5;
if (analysis.hasNumbers) score += 5;
if (analysis.hasSpecialChars) score += 10;
// 다양성 보너스
const characterSets = [
analysis.hasLowercase,
analysis.hasUppercase,
analysis.hasNumbers,
analysis.hasSpecialChars
].filter(Boolean).length;
if (characterSets >= 3) score += 10;
if (characterSets === 4) score += 15;
return score;
}
calculatePatternPenalty(analysis) {
let penalty = 0;
if (analysis.hasRepeating) penalty -= 15;
if (analysis.hasSequential) penalty -= 10;
if (analysis.isCommonWord) penalty -= 25;
return penalty;
}
calculateEntropy(password) {
// 문자 집합 크기 계산
let charsetSize = 0;
if (this.patterns.lowercase.test(password)) charsetSize += 26;
if (this.patterns.uppercase.test(password)) charsetSize += 26;
if (this.patterns.numbers.test(password)) charsetSize += 10;
if (this.patterns.specialChars.test(password)) charsetSize += 32;
// 엔트로피 = log2(charsetSize^length)
return password.length * Math.log2(charsetSize);
}
estimateBreakTime(analysis) {
// 현대적인 GPU 기준: 초당 10^9 회 시도
const attemptsPerSecond = 1000000000;
const keySpace = Math.pow(95, analysis.length); // 95개 인쇄 가능한 ASCII 문자
const averageAttempts = keySpace / 2;
const secondsToBreak = averageAttempts / attemptsPerSecond;
return this.formatTime(secondsToBreak);
}
formatTime(seconds) {
if (seconds < 1) return '즉시';
if (seconds < 60) return `${Math.round(seconds)}초`;
if (seconds < 3600) return `${Math.round(seconds / 60)}분`;
if (seconds < 86400) return `${Math.round(seconds / 3600)}시간`;
if (seconds < 31536000) return `${Math.round(seconds / 86400)}일`;
return `${Math.round(seconds / 31536000)}년`;
}
classifyStrength(score) {
if (score < 20) return 'very_weak';
if (score < 40) return 'weak';
if (score < 60) return 'fair';
if (score < 80) return 'good';
return 'very_strong';
}
generateImprovements(analysis) {
const improvements = [];
if (analysis.length < 12) {
improvements.push('비밀번호를 12자리 이상으로 늘리세요');
}
if (!analysis.hasUppercase) {
improvements.push('대문자를 추가하세요');
}
if (!analysis.hasLowercase) {
improvements.push('소문자를 추가하세요');
}
if (!analysis.hasNumbers) {
improvements.push('숫자를 추가하세요');
}
if (!analysis.hasSpecialChars) {
improvements.push('특수문자를 추가하세요 (!@#$%^&* 등)');
}
if (analysis.hasRepeating) {
improvements.push('반복되는 문자를 피하세요');
}
if (analysis.hasSequential) {
improvements.push('연속된 문자나 숫자를 피하세요');
}
if (analysis.isCommonWord) {
improvements.push('일반적인 단어나 패턴을 피하세요');
}
return improvements;
}
isCommonPassword(password) {
const lowerPassword = password.toLowerCase();
return this.patterns.commonWords.some(word => lowerPassword.includes(word));
}
}
// 사용 예시
const analyzer = new PasswordStrengthAnalyzer();
const result = analyzer.analyzeStrength('MyP@ssw0rd123!');
console.log(result);
// {
// score: 75,
// strength: 'good',
// entropy: 65.4,
// timeToBreak: '34,000년',
// improvements: ['비밀번호를 12자리 이상으로 늘리세요']
// }
2. 강력한 비밀번호 생성 방법
안전한 비밀번호 생성 전략
1. Passphrase (패스프레이즈) 방식
class PassphraseGenerator {
constructor() {
// 다양한 언어와 주제의 단어 목록
this.wordLists = {
korean: ['바다', '산', '하늘', '꽃', '나무', '별', '달', '구름', '강', '호수'],
english: ['ocean', 'mountain', 'sky', 'flower', 'tree', 'star', 'moon', 'cloud', 'river', 'lake'],
adjectives: ['빠른', '느린', '큰', '작은', '밝은', '어두운', '뜨거운', '차가운', '부드러운', '단단한'],
verbs: ['달리다', '걷다', '뛰다', '날다', '수영하다', '춤추다', '노래하다', '읽다', '쓰다', '그리다'],
colors: ['빨강', '파랑', '노랑', '초록', '보라', '주황', '분홍', '검정', '하양', '회색'],
numbers: ['하나', '둘', '셋', '넷', '다섯', '여섯', '일곱', '여덟', '아홉', '열']
};
this.separators = ['!', '@', '#', '$', '%', '^', '&', '*', '-', '_', '+', '='];
}
generatePassphrase(options = {}) {
const {
wordCount = 4,
includeNumbers = true,
includeSymbols = true,
capitalizeFirst = true,
customSeparator = null,
language = 'korean'
} = options;
const words = [];
const availableWords = this.wordLists[language] || this.wordLists.korean;
// 단어 선택
for (let i = 0; i < wordCount; i++) {
let word = this.getRandomElement(availableWords);
if (capitalizeFirst && i === 0) {
word = word.charAt(0).toUpperCase() + word.slice(1);
}
words.push(word);
}
// 구분자 선택
let separator = customSeparator || (includeSymbols ?
this.getRandomElement(this.separators) : '');
// 숫자 추가
if (includeNumbers) {
const randomNumber = Math.floor(Math.random() * 9999) + 1;
words.push(randomNumber.toString());
}
return {
passphrase: words.join(separator),
words: words,
separator: separator,
strength: this.estimatePassphraseStrength(words.join(separator)),
memorabilityTips: this.generateMemorabilityTips(words)
};
}
generateMemorabilityTips(words) {
return [
`스토리 만들기: "${words.slice(0, -1).join(' ')}으로 ${words[words.length-1]}를 세어보세요"`,
`첫 글자로 문장 만들기: ${words.map(w => w.charAt(0)).join('')}`,
`리듬으로 기억하기: 단어들을 노래나 랩으로 만들어 보세요`,
`시각적 연상: 각 단어를 그림으로 그려보고 연결해보세요`
];
}
getRandomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}
estimatePassphraseStrength(passphrase) {
const analyzer = new PasswordStrengthAnalyzer();
return analyzer.analyzeStrength(passphrase);
}
}
// 사용 예시
const passphraseGen = new PassphraseGenerator();
const result = passphraseGen.generatePassphrase({
wordCount: 4,
includeNumbers: true,
includeSymbols: true,
language: 'korean'
});
console.log(result);
// {
// passphrase: "바다!산!하늘!꽃!2847",
// words: ["바다", "산", "하늘", "꽃", "2847"],
// separator: "!",
// strength: { score: 85, strength: 'very_strong' },
// memorabilityTips: [...]
// }
2. 패턴 기반 생성 방식
class PatternBasedGenerator {
constructor() {
this.patterns = {
// 위치별 문자 유형 지정
positional: [
'U', // Uppercase letter
'l', // lowercase letter
'l', // lowercase letter
'd', // digit
's', // symbol
'U', // Uppercase letter
'l', // lowercase letter
'd', // digit
'd', // digit
's', // symbol
'l', // lowercase letter
'l' // lowercase letter
],
// 청크 기반 패턴 (기억하기 쉬운 덩어리)
chunks: [
{ type: 'word', case: 'title' }, // 첫글자 대문자 단어
{ type: 'number', length: 2 }, // 2자리 숫자
{ type: 'symbol', count: 1 }, // 특수문자 1개
{ type: 'word', case: 'lower' }, // 소문자 단어
{ type: 'number', length: 2 } // 2자리 숫자
]
};
this.characterSets = {
uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
lowercase: 'abcdefghijklmnopqrstuvwxyz',
digits: '0123456789',
symbols: '!@#$%^&*()_+-=[]{}|;:,.<>?',
safe_symbols: '!@#$%^&*-_=+' // 대부분 시스템에서 안전한 특수문자
};
this.commonWords = {
short: ['cat', 'dog', 'sun', 'moon', 'star', 'blue', 'red', 'joy', 'hope', 'love'],
medium: ['ocean', 'forest', 'mountain', 'rainbow', 'thunder', 'whisper', 'shadow', 'crystal']
};
}
generateByPattern(patternType = 'positional', options = {}) {
const {
useCommonWords = true,
avoidAmbiguous = true, // 0, O, l, 1 등 헷갈리기 쉬운 문자 제외
customPattern = null
} = options;
if (avoidAmbiguous) {
this.removeAmbiguousChars();
}
const pattern = customPattern || this.patterns[patternType];
if (patternType === 'positional') {
return this.generatePositionalPattern(pattern);
} else if (patternType === 'chunks') {
return this.generateChunkPattern(pattern, useCommonWords);
}
}
generatePositionalPattern(pattern) {
let password = '';
const usedChars = new Set(); // 중복 방지를 위한 Set
pattern.forEach(charType => {
let char;
let attempts = 0;
do {
char = this.getRandomCharByType(charType);
attempts++;
} while (usedChars.has(char) && attempts < 10);
usedChars.add(char);
password += char;
});
return {
password,
pattern: pattern.join(''),
analysis: new PasswordStrengthAnalyzer().analyzeStrength(password),
generationMethod: 'positional'
};
}
generateChunkPattern(chunks, useCommonWords) {
const parts = [];
let password = '';
chunks.forEach(chunk => {
let part = '';
switch (chunk.type) {
case 'word':
if (useCommonWords) {
part = this.getRandomElement(this.commonWords.short);
if (chunk.case === 'title') {
part = part.charAt(0).toUpperCase() + part.slice(1);
} else if (chunk.case === 'upper') {
part = part.toUpperCase();
}
} else {
// 무작위 문자로 단어 모양 생성
const length = Math.floor(Math.random() * 4) + 3; // 3-6자
for (let i = 0; i < length; i++) {
if (i === 0 && chunk.case === 'title') {
part += this.getRandomCharByType('U');
} else {
part += this.getRandomCharByType('l');
}
}
}
break;
case 'number':
for (let i = 0; i < chunk.length; i++) {
part += this.getRandomCharByType('d');
}
break;
case 'symbol':
for (let i = 0; i < chunk.count; i++) {
part += this.getRandomCharByType('s');
}
break;
}
parts.push(part);
password += part;
});
return {
password,
chunks: parts,
chunkTypes: chunks.map(c => c.type),
analysis: new PasswordStrengthAnalyzer().analyzeStrength(password),
generationMethod: 'chunks',
memorabilityPattern: this.createMemorabilityPattern(parts, chunks)
};
}
createMemorabilityPattern(parts, chunks) {
const patterns = [];
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const chunk = chunks[i];
if (chunk.type === 'word') {
patterns.push(`단어: ${part}`);
} else if (chunk.type === 'number') {
patterns.push(`숫자: ${part} (${this.numberToKorean(part)})`);
} else if (chunk.type === 'symbol') {
patterns.push(`기호: ${part}`);
}
}
return patterns;
}
numberToKorean(number) {
const korean = ['영', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
return number.split('').map(digit => korean[parseInt(digit)]).join('');
}
getRandomCharByType(type) {
let charset = '';
switch (type) {
case 'U': charset = this.characterSets.uppercase; break;
case 'l': charset = this.characterSets.lowercase; break;
case 'd': charset = this.characterSets.digits; break;
case 's': charset = this.characterSets.safe_symbols; break;
}
return charset.charAt(Math.floor(Math.random() * charset.length));
}
removeAmbiguousChars() {
// 헷갈리기 쉬운 문자 제거
this.characterSets.uppercase = this.characterSets.uppercase.replace(/[0OIL]/g, '');
this.characterSets.lowercase = this.characterSets.lowercase.replace(/[l1]/g, '');
this.characterSets.digits = this.characterSets.digits.replace(/[01]/g, '');
}
getRandomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}
}
// 사용 예시
const patternGen = new PatternBasedGenerator();
// 위치별 패턴 생성
const positionalResult = patternGen.generateByPattern('positional');
console.log(positionalResult);
// { password: "Ab3!De78*fg", pattern: "UllddsUldds", analysis: {...} }
// 청크 패턴 생성
const chunkResult = patternGen.generateByPattern('chunks');
console.log(chunkResult);
// {
// password: "Cat42@moon89",
// chunks: ["Cat", "42", "@", "moon", "89"],
// memorabilityPattern: ["단어: Cat", "숫자: 42 (사이)", "기호: @", ...]
// }
비밀번호 생성 도구 비교
생성 방식별 장단점
const generationMethods = {
random: {
pros: ['최고 수준의 보안', '예측 불가능', '무차별 대입 공격에 강함'],
cons: ['기억하기 어려움', '입력 오류 가능성 높음', '사용자 불편'],
useCases: ['시스템 간 통신', 'API 키', '일회성 계정'],
example: 'Kj8#mP2$qR9!',
memorability: 1,
security: 10
},
passphrase: {
pros: ['기억하기 쉬움', '긴 길이로 높은 보안', '입력 오류 적음'],
cons: ['사전 공격 취약 가능성', '언어별 패턴 존재', '길이가 길어짐'],
useCases: ['개인 계정', '마스터 비밀번호', '자주 사용하는 계정'],
example: 'correct-horse-battery-staple-47',
memorability: 9,
security: 8
},
pattern: {
pros: ['보안과 기억 가능성 균형', '일관된 형식', '교육 효과'],
cons: ['패턴 노출 시 취약', '창의성 제한', '시스템별 호환성'],
useCases: ['기업 정책', '교육용', '다중 계정 관리'],
example: 'Work2024!',
memorability: 7,
security: 7
},
mnemonic: {
pros: ['개인화 가능', '의미 있는 연결', '장기 기억 용이'],
cons: ['개인 정보 노출 위험', '패턴 추측 가능', '복잡도 제한'],
useCases: ['개인용', '중요도 중간 계정', '복구 질문 대체'],
example: 'MyDog3Years@Home', // "My Dog is 3 Years old At Home"
memorability: 8,
security: 6
}
};
3. 비밀번호 관리 전략
체계적인 비밀번호 관리
계정별 중요도 분류
class PasswordTierSystem {
constructor() {
this.tiers = {
critical: {
description: '금융, 업무, 중요 개인정보 관련',
requirements: {
minLength: 16,
complexity: 'very_strong',
uniqueness: true, // 다른 곳에서 절대 재사용 금지
mfa: true, // 2단계 인증 필수
changeFrequency: 90 // 90일마다 변경
},
examples: ['은행', '증권사', '회사 계정', '클라우드 스토리지', '이메일'],
storageMethod: 'password_manager_only'
},
important: {
description: '소셜미디어, 쇼핑몰, 구독 서비스',
requirements: {
minLength: 12,
complexity: 'strong',
uniqueness: true,
mfa: 'recommended',
changeFrequency: 180
},
examples: ['페이스북', '인스타그램', '쿠팡', '넷플릭스', '유튜브'],
storageMethod: 'password_manager_preferred'
},
standard: {
description: '일반 웹사이트, 포럼, 뉴스 사이트',
requirements: {
minLength: 10,
complexity: 'good',
uniqueness: 'recommended',
mfa: 'optional',
changeFrequency: 365
},
examples: ['뉴스 사이트', '커뮤니티', '무료 도구', '일회성 가입'],
storageMethod: 'browser_ok'
},
low_risk: {
description: '테스트용, 임시 계정',
requirements: {
minLength: 8,
complexity: 'fair',
uniqueness: false,
mfa: false,
changeFrequency: 'as_needed'
},
examples: ['테스트 사이트', '일회성 다운로드', '임시 계정'],
storageMethod: 'simple_pattern_ok'
}
};
}
classifyAccount(accountInfo) {
const { domain, accountType, dataTypes, financialAccess } = accountInfo;
// 금융 관련
if (financialAccess ||
['bank', 'finance', 'payment', 'crypto'].includes(accountType)) {
return 'critical';
}
// 개인정보나 업무 관련
if (dataTypes.includes('personal_info') ||
dataTypes.includes('work_data') ||
['email', 'cloud_storage', 'work'].includes(accountType)) {
return 'critical';
}
// 소셜미디어, 주요 서비스
if (['social', 'shopping', 'subscription'].includes(accountType) ||
['facebook.com', 'instagram.com', 'twitter.com', 'linkedin.com'].includes(domain)) {
return 'important';
}
// 일반 웹사이트
if (accountType === 'general' || dataTypes.length === 0) {
return 'standard';
}
return 'low_risk';
}
generatePasswordForTier(tier, options = {}) {
const requirements = this.tiers[tier].requirements;
const generator = new PatternBasedGenerator();
let password;
let attempts = 0;
do {
if (tier === 'critical') {
// 최고 보안: 완전 무작위
password = this.generateCryptoSecurePassword(requirements.minLength);
} else if (tier === 'important') {
// 패스프레이즈 방식
const passphraseGen = new PassphraseGenerator();
const result = passphraseGen.generatePassphrase({
wordCount: 3,
includeNumbers: true,
includeSymbols: true
});
password = result.passphrase;
} else {
// 패턴 기반
const result = generator.generateByPattern('chunks');
password = result.password;
}
attempts++;
} while (!this.meetsRequirements(password, requirements) && attempts < 10);
return {
password,
tier,
requirements,
analysis: new PasswordStrengthAnalyzer().analyzeStrength(password),
recommendations: this.getManagementRecommendations(tier)
};
}
generateCryptoSecurePassword(length) {
const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*';
let password = '';
// crypto.getRandomValues() 사용 (브라우저 환경)
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
const array = new Uint8Array(length);
crypto.getRandomValues(array);
for (let i = 0; i < length; i++) {
password += charset[array[i] % charset.length];
}
} else {
// 폴백: Math.random() (보안성이 떨어지므로 경고)
console.warn('암호학적으로 안전한 난수 생성기를 사용할 수 없습니다.');
for (let i = 0; i < length; i++) {
password += charset[Math.floor(Math.random() * charset.length)];
}
}
return password;
}
meetsRequirements(password, requirements) {
const analysis = new PasswordStrengthAnalyzer().analyzeStrength(password);
return password.length >= requirements.minLength &&
this.strengthToScore(analysis.strength) >= this.strengthToScore(requirements.complexity);
}
strengthToScore(strength) {
const scoreMap = {
'very_weak': 1,
'weak': 2,
'fair': 3,
'good': 4,
'strong': 5,
'very_strong': 6
};
return scoreMap[strength] || 0;
}
getManagementRecommendations(tier) {
const tierInfo = this.tiers[tier];
return [
`보관 방법: ${tierInfo.storageMethod}`,
`2단계 인증: ${tierInfo.requirements.mfa ? '필수' : '권장'}`,
`변경 주기: ${tierInfo.requirements.changeFrequency}일`,
`고유성: ${tierInfo.requirements.uniqueness ? '필수' : '권장'}`,
`최소 길이: ${tierInfo.requirements.minLength}자`
];
}
}
// 사용 예시
const tierSystem = new PasswordTierSystem();
// 계정 분류
const accountInfo = {
domain: 'mybank.com',
accountType: 'bank',
dataTypes: ['financial', 'personal_info'],
financialAccess: true
};
const tier = tierSystem.classifyAccount(accountInfo);
console.log(`계정 등급: ${tier}`); // "critical"
// 해당 등급에 맞는 비밀번호 생성
const passwordResult = tierSystem.generatePasswordForTier(tier);
console.log(passwordResult);
비밀번호 매니저 활용
비밀번호 매니저 선택 기준
const passwordManagerComparison = {
// 개인용 추천
personal: {
'Bitwarden': {
pros: ['오픈소스', '무료 버전 충분', '크로스 플랫폼', '자체 호스팅 가능'],
cons: ['UI가 다소 복잡', '고급 기능은 유료'],
price: '무료 / $3/월',
security: '최고급',
usability: '중급',
recommendation: '개인 사용자에게 최고의 선택'
},
'1Password': {
pros: ['직관적 UI', '강력한 보안', '가족 공유', '여행 모드'],
cons: ['무료 버전 없음', '상대적으로 비쌈'],
price: '$3-8/월',
security: '최고급',
usability: '최고급',
recommendation: '사용 편의성 중시 시 선택'
},
'LastPass': {
pros: ['무료 버전', '브라우저 통합', '쉬운 사용법'],
cons: ['과거 보안 사고', '무료 버전 제한'],
price: '무료 / $3/월',
security: '양호',
usability: '고급',
recommendation: '입문자용으로 적합'
}
},
// 기업용 추천
enterprise: {
'Bitwarden Business': {
features: ['SSO 지원', '관리자 콘솔', '감사 로그', '정책 관리'],
price: '$3/사용자/월',
deployment: '클라우드/온프레미스',
compliance: 'SOC 2, GDPR, HIPAA'
},
'CyberArk': {
features: ['PAM 통합', '권한 관리', '세션 모니터링', '위협 분석'],
price: '문의',
deployment: '온프레미스/하이브리드',
compliance: '금융업 특화'
}
}
};
// 비밀번호 매니저 통합 클래스
class PasswordManagerIntegration {
constructor(managerType = 'generic') {
this.managerType = managerType;
this.vault = new Map(); // 로컬 시뮬레이션용
this.categories = ['critical', 'important', 'standard', 'low_risk'];
}
// 비밀번호 저장
storePassword(entry) {
const {
title,
username,
password,
url,
category = 'standard',
notes = '',
customFields = {}
} = entry;
const passwordEntry = {
id: this.generateId(),
title,
username,
password: this.encrypt(password), // 실제로는 매니저가 암호화
url,
category,
notes,
customFields,
createdAt: new Date(),
lastModified: new Date(),
lastUsed: null,
strength: new PasswordStrengthAnalyzer().analyzeStrength(password),
tags: this.generateTags(url, category)
};
this.vault.set(passwordEntry.id, passwordEntry);
return passwordEntry.id;
}
// 비밀번호 검색
searchPasswords(query) {
const results = [];
this.vault.forEach(entry => {
const searchText = `${entry.title} ${entry.username} ${entry.url} ${entry.notes}`.toLowerCase();
if (searchText.includes(query.toLowerCase())) {
results.push({
...entry,
password: this.decrypt(entry.password) // 실제로는 마스터 비밀번호 검증 후
});
}
});
return results.sort((a, b) => b.lastUsed - a.lastUsed); // 최근 사용 순
}
// 취약한 비밀번호 감지
auditPasswords() {
const audit = {
weak: [],
reused: [],
old: [],
compromised: [],
total: this.vault.size
};
const passwords = new Map(); // 중복 체크용
const currentDate = new Date();
this.vault.forEach(entry => {
const decryptedPassword = this.decrypt(entry.password);
// 약한 비밀번호
if (entry.strength.score < 60) {
audit.weak.push({
title: entry.title,
score: entry.strength.score,
improvements: entry.strength.improvements
});
}
// 재사용 비밀번호
if (passwords.has(decryptedPassword)) {
const existing = passwords.get(decryptedPassword);
audit.reused.push({
password: '***hidden***',
accounts: [existing.title, entry.title]
});
} else {
passwords.set(decryptedPassword, entry);
}
// 오래된 비밀번호 (1년 이상)
const daysSinceModified = (currentDate - entry.lastModified) / (1000 * 60 * 60 * 24);
if (daysSinceModified > 365) {
audit.old.push({
title: entry.title,
lastModified: entry.lastModified,
daysOld: Math.floor(daysSinceModified)
});
}
// 유출된 비밀번호 체크 (시뮬레이션)
if (this.isCompromised(decryptedPassword)) {
audit.compromised.push({
title: entry.title,
breach: 'Multiple breaches found'
});
}
});
return audit;
}
// 자동 비밀번호 생성 및 변경
autoUpdatePassword(entryId, options = {}) {
const entry = this.vault.get(entryId);
if (!entry) return null;
const tierSystem = new PasswordTierSystem();
const tier = tierSystem.classifyAccount({
domain: this.extractDomain(entry.url),
accountType: entry.category,
dataTypes: entry.customFields.dataTypes || [],
financialAccess: entry.tags.includes('financial')
});
const newPasswordResult = tierSystem.generatePasswordForTier(tier, options);
// 이전 비밀번호 백업
const backup = {
password: entry.password,
changedAt: new Date(),
reason: 'auto_update'
};
// 새 비밀번호 저장
entry.password = this.encrypt(newPasswordResult.password);
entry.lastModified = new Date();
entry.strength = newPasswordResult.analysis;
entry.previousPasswords = entry.previousPasswords || [];
entry.previousPasswords.push(backup);
// 최대 3개까지만 백업 유지
if (entry.previousPasswords.length > 3) {
entry.previousPasswords.shift();
}
return {
newPassword: newPasswordResult.password,
strength: newPasswordResult.analysis,
recommendations: newPasswordResult.recommendations
};
}
// 보안 점수 계산
calculateSecurityScore() {
if (this.vault.size === 0) return 0;
let totalScore = 0;
let criticalAccountsSecure = 0;
let totalCriticalAccounts = 0;
this.vault.forEach(entry => {
const baseScore = Math.min(entry.strength.score, 100);
let multiplier = 1;
// 카테고리별 가중치
if (entry.category === 'critical') {
multiplier = 2;
totalCriticalAccounts++;
if (baseScore >= 80) criticalAccountsSecure++;
} else if (entry.category === 'important') {
multiplier = 1.5;
}
totalScore += baseScore * multiplier;
});
const averageScore = totalScore / this.vault.size;
const criticalSecurityRatio = totalCriticalAccounts > 0 ?
criticalAccountsSecure / totalCriticalAccounts : 1;
return {
overallScore: Math.round(averageScore * criticalSecurityRatio),
criticalAccountsSecurity: Math.round(criticalSecurityRatio * 100),
recommendations: this.getSecurityRecommendations(averageScore, criticalSecurityRatio)
};
}
getSecurityRecommendations(averageScore, criticalRatio) {
const recommendations = [];
if (averageScore < 70) {
recommendations.push('전체적인 비밀번호 강도를 높이세요');
}
if (criticalRatio < 0.9) {
recommendations.push('중요 계정의 비밀번호를 더욱 강화하세요');
}
const auditResults = this.auditPasswords();
if (auditResults.weak.length > 0) {
recommendations.push(`${auditResults.weak.length}개의 약한 비밀번호를 교체하세요`);
}
if (auditResults.reused.length > 0) {
recommendations.push(`${auditResults.reused.length}개의 재사용 비밀번호를 변경하세요`);
}
if (auditResults.old.length > 0) {
recommendations.push(`${auditResults.old.length}개의 오래된 비밀번호를 업데이트하세요`);
}
return recommendations;
}
// 유틸리티 메서드들
generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
encrypt(password) {
// 실제로는 AES-256 등 강력한 암호화 사용
return btoa(password); // Base64는 예시용
}
decrypt(encryptedPassword) {
return atob(encryptedPassword);
}
extractDomain(url) {
try {
return new URL(url).hostname;
} catch {
return url;
}
}
generateTags(url, category) {
const tags = [category];
const domain = this.extractDomain(url);
// 도메인 기반 태그
if (domain.includes('bank') || domain.includes('finance')) {
tags.push('financial');
}
if (domain.includes('work') || domain.includes('company')) {
tags.push('work');
}
if (domain.includes('social') || ['facebook', 'twitter', 'instagram'].some(s => domain.includes(s))) {
tags.push('social');
}
return tags;
}
isCompromised(password) {
// 실제로는 HaveIBeenPwned API 등을 사용
const commonCompromised = ['password123', '123456', 'password', 'admin'];
return commonCompromised.includes(password.toLowerCase());
}
}