취약한 비밀번호 복구 절차 (PR) | 위험도: 4 (높음) | 통제구분: 5.2.3 인증 관리
인터넷뱅킹 비밀번호 찾기
가입하신 이메일 주소를 입력하시면 임시 비밀번호를 발송해 드립니다.
// ❌ 취약한 비밀번호 복구 로직
@PostMapping("/findPassword")
public String findPassword(String userId,
String email) {
User user = userRepository.findByUserId(userId);
// 이메일만 확인 (본인인증 없음!)
if (user.getEmail().equals(email)) {
// 예측 가능한 임시 비밀번호
String tempPwd = "temp1234";
user.setPassword(tempPwd);
userRepository.save(user);
// 이메일 발송 (평문)
sendEmail(email, "임시 비밀번호: " + tempPwd);
return "success";
}
return "fail";
}
공격 시나리오를 선택하세요
// ✅ 안전한 비밀번호 복구 로직
@PostMapping("/findPassword")
public String findPassword(String userId,
String email,
String phone) {
User user = userRepository.findByUserId(userId);
// 1. 본인인증 강화 (이메일 + 휴대폰)
if (!user.getEmail().equals(email) ||
!user.getPhone().equals(phone)) {
return "인증 실패";
}
// 2. SMS/이메일 인증코드 발송
String authCode = generateSecureToken(6);
saveAuthCode(userId, authCode, 10); // 10분 유효
sendSMS(phone, "인증코드: " + authCode);
return "redirect:/verifyAuthCode";
}
@PostMapping("/resetPassword")
public String resetPassword(String userId,
String authCode,
String newPassword) {
// 3. 인증코드 검증 (시간 제한)
if (!verifyAuthCode(userId, authCode)) {
return "인증코드 만료 또는 불일치";
}
// 4. 비밀번호 강도 검증
if (!isStrongPassword(newPassword)) {
return "비밀번호 강도 미달";
}
// 5. 비밀번호 암호화 저장
String hashedPwd = BCrypt.hashpw(newPassword);
user.setPassword(hashedPwd);
userRepository.save(user);
// 6. 기존 모든 세션 무효화
sessionRegistry.expireAllSessions(userId);
// 7. 알림 발송
sendEmail(email, "비밀번호가 변경되었습니다");
return "success";
}
// 안전한 토큰 생성
private String generateSecureToken(int length) {
SecureRandom random = new SecureRandom();
return random.ints(length, 0, 10)
.mapToObj(String::valueOf)
.collect(Collectors.joining());
}