전자금융기반시설 보안 취약점 평가기준 - WEB-SER-042

취약한 비밀번호 복구 절차 (PR) | 위험도: 4 (높음) | 통제구분: 5.2.3 인증 관리

잭은행

인터넷뱅킹 비밀번호 찾기

취약점: 본인 인증 절차 미흡

비밀번호 찾기

가입하신 이메일 주소를 입력하시면 임시 비밀번호를 발송해 드립니다.

공격 시나리오

취약한 Java 코드

위험
// ❌ 취약한 비밀번호 복구 로직
@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";
}

공격 실행 결과

대기중

공격 시나리오를 선택하세요

안전한 Java 코드

권장
// ✅ 안전한 비밀번호 복구 로직
@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());
}

비밀번호 복구 보안 체크리스트

다중 본인인증: 이메일 + SMS + 보안질문
시간 제한 토큰: 10분 이내 인증코드
안전한 난수 생성: SecureRandom 사용
세션 무효화: 재설정 후 모든 세션 종료
알림 발송: 변경 완료 이메일/SMS
재사용 방지: 인증코드 일회용