
CSAP 점검을 받던 도중 비밀번호 암호화 알고리즘을 변경해야 한다는 지적사항을 받았다.
기존에는 BCrypt 알고리즘을 사용하고 있었지만, CSAP 기준에는 맞지 않는다고 한다. (SHA256 이상 단방향 암호화 알고리즘을 적용해야 한다고 하는데... 암호화 강도는 BCrypt가 더 높다고 알려져있는데 기준에 안 맞는 이유를 모르겠다)
문제는 기존에 이미 BCrypt 암호화 알고리즘으로 비밀번호를 저장한 계정들이 있었는데 정말 무식하게 이야기하면 계정들의 비밀번호를 강제로 초기화시키는 방법을 쓰면 되겠지만 운영 중에 이런 이슈가 생기지 않으리라는 보장이 없기에 다른 방법이 있는지 확인을 하게 되었다.
그리고 역시나 Spring Security에서는 이렇게 암호화 알고리즘을 마이그레이션하거나 업데이트해야 하는 상황을 위해 DelegatingPasswordEncoder를 제공하고 있었다는 사실을 알았다.
DelegatingPasswordEncoder는 Spring Security 5.0 이후에 도입되었다고 하며, 다음과 같은 기능을 제공한다.
DelegatingPasswordEncoder는 이름 그대로 비밀번호 인코딩을 다른 인코더에 위임을 하는 역할을 한다. 각자 다른 암호화 방식을 적용하는 인코더들을 등록하고 비밀번호를 인코딩해서 등록할 때 인코더의 식별자를 앞에 붙여 해당 비밀번호가 어떤 인코더를 통해 등록되었는지를 식별하도록 해준다.
// DelegatingPasswordEncoder에
// BcryptPasswordEncoder, StandardPasswordEncoder를 식별자와 함께 등록한 후
// 각자 다른 인코더에 위임해 비밀번호를 인코딩해서 저장했을 경우
{bcrypt}블라블라블라블라대충인코딩된비밀번호1 // BcryptPasswordEncoder로 인코딩된 비밀번호
{sha256}블라블라블라블라대충인코딩된비밀번호2 // StandardPasswordEncoder로 인코딩된 비밀번호
기존 PasswordEncoder Bean 등록부를 다음과 같이 설정한다.
@Bean
public PasswordEncoder passwordEncoder() {
// Encoder Map 생성
Map<String, PasswordEncoder> encoders = new HashMap<>();
// 기존에 사용하던 BCryptPasswordEncoder
PasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// Encoder Map에 BCryptPasswordEncoder와 직접 만든 SHA512 인코더 등록
encoders.put("sha512", new Sha512PasswordEncoder());
encoders.put("bcrypt", bCryptPasswordEncoder);
// DelegatingPasswordEncoder 등록, 기본 인코더는 Sha512PasswordEncoder를 사용하도록 지정
DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder("sha512", encoders);
// DelegatingPasswordEncoder로 교체하기 전에 식별자가 지정되지 않았던 시점의 비밀번호들은
// 그 시점에 적용되었던 PasswordEncoder를 기본적으로 매칭시키게끔 설정
delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(bCryptPasswordEncoder);
return delegatingPasswordEncoder;
}
여기서 주의할 점은 기존에는 BCryptPasswordEncoder를 사용하고 있었는데, 이 때 인코딩된 비밀번호의 앞에는 인코더의 식별자가 존재하지 않는다. 따라서 식별자가 존재하지 않는 인코딩된 비밀번호에 대해 기본적으로 매칭시킬 인코더를 지정해줘야 한다.
이 부분은 delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(bCryptPasswordEncoder); 로 처리하였다. 즉, DelegatingPasswordEncoder로 교체하기 전에 식별자가 지정되지 않았던 시점의 비밀번호들은 그 시점에 적용되었던 PasswordEncoder를 기본적으로 매칭시키게끔 설정하면 된다.
이 상태에서 기존의 알고리즘으로 비밀번호가 저장되었던 계정은 비밀번호를 수정하면 자연스럽게 현재 적용되는 기본 알고리즘으로 변경되게 된다.