issue 기록
BCryptPasswordEncoder 패스워드 암호화 관련 이슈
공부절대안함
2021. 7. 23. 17:21
문제점
스프링 시큐리티를 활용하기 위해서는 DB에 해쉬로 암호화된 패스워드를 저장해야 한다.
따라서 BCryptPasswordEncoder에 encode 메서드를 통해 해쉬암호화를 사용하여 패스워드를 저장했다.
문제는 패스워드 변경 관련 로직 및 테스트 코드 작성에서 발생했다.
@Test
public void 회원정보_업데이트_테스트() throws Exception {
//given
User user = userRepository.findByName("test").orElseThrow(NoSuchUserException::new);
UpdatePasswordDTO updatePasswordDTO = new UpdatePasswordDTO("12345", "1234", "1234");
//when
userService.updateName(updateNameDto,user.getId());
userService.updatePassword(updatePasswordDTO, user.getId());
//then
assertThat(bCryptPasswordEncoder.encode("1234")).isEqualTo(user.getPassword());
}
기존 회원의 패스워드를 변경하고 잘 변경되었는지 확인하는 테스트 코드이다.
비밀번호를 "12345"->"1234"로 변경하고 junit을 활용하여 확인해보았지만 계속 실패했다.
원인
원인을 파악하기 위해 여러가지를 찍어보던중 해쉬로 encode되는 값이 매번 달라지는 것을 알아냈다.
System.out.println(result.getPassword());
System.out.println(bCryptPasswordEncoder.encode("12345"));
같아야 하는 값이 계속 다르게 출력되고 있었다. 그래서 BCrypt에 대해서 조사해봤다.
BCrypt는 해쉬에 솔트를 더하여 매번 다르게 값이 출력되게 만들어서 보안을 더 높인 암호화 방식이다.
따라서 솔트가 매번 다르기 때문에 encode된 값과 equals로 비교가 항상 다르기 때문에 테스트가 통과될 수 없는 구조였다. 해쉬를 하면 매번 같은 값이 나올것이라는 착각이였다.
해결방법
equals로 비교할 수 없는 BCryptPasswordEncoder은 비교 메서드 matches를 제공한다.
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("rawPassword cannot be null");
} else if (encodedPassword != null && encodedPassword.length() != 0) {
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
this.logger.warn("Encoded password does not look like BCrypt");
return false;
} else {
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
} else {
this.logger.warn("Empty encoded password");
return false;
}
}
따라서 테스트 코드를 다음과 같이 짜주면 해결할 수 있다.
assertThat(bCryptPasswordEncoder.matches("1234", result.getPassword())).isEqualTo(true);
assertThat(bCryptPasswordEncoder.matches("12345", result.getPassword())).isEqualTo(false);
배운점
- 해쉬를 한번 더 공부하게 되었다.
- 사용하는 기능에 대해서 잘 모르고 사용하는게 위험하다는 것을 또 느꼈다. 항상 제대로 알고 사용하자