1. 개요

Spring Security 4에서는 메모리 내 인증을 사용하여 일반 텍스트로 비밀번호를 저장할 수 있었습니다.

버전 5의 암호 관리 프로세스에 대한 대대적인 점검으로 암호 인코딩 및 디코딩을 위한 보다 안전한 기본 메커니즘이 도입되었습니다. 이는 Spring 애플리케이션이 일반 텍스트로 비밀번호를 저장하는 경우 Spring Security 5로 업그레이드하면 문제가 발생할 수 있음을 의미합니다.

이 짧은 사용방법(예제)에서는 잠재적인 문제 중 하나를 설명하고 솔루션을 시연합니다.

2. 스프링 시큐리티 4

간단한 메모리 내 인증(Spring 4에 유효)을 제공하는 표준 Security 구성을 보여줌으로써 시작하겠습니다.

@Configuration
public class InMemoryAuthWebSecurityConfigurer 
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
      throws Exception {
        auth.inMemoryAuthentication()
          .withUser("spring")
          .password("secret")
          .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          .antMatchers("/private/**")
          .authenticated()
          .antMatchers("/public/**")
          .permitAll()
          .and()
          .httpBasic();
    }
}

이 구성은 모든 /private/ 매핑된 메서드에 대한 인증과 /public/ 아래의 모든 항목에 대한 공용 액세스를 정의합니다.

Spring Security 5에서 동일한 구성을 사용하면 다음 오류가 발생합니다.

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

이 오류는 메모리 내 인증을 위해 구성된 비밀번호 인코더가 없기 때문에 지정된 비밀번호를 디코딩할 수 없음을 알려줍니다 .

3. 스프링 시큐리티 5

PasswordEncoderFactories 클래스 로 Delegating PasswordEncoder정의하여 이 오류를 수정할 수 있습니다 .

이 인코더를 사용하여 사용자를 구성합니다.

@Configuration
public class InMemoryAuthWebSecurityConfigurer {

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        UserDetails user = User.withUsername("spring")
            .password(encoder.encode("secret"))
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
}

이제 이 구성으로 BCrypt를 사용하여 메모리 내 암호를 다음 형식으로 저장합니다.

{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS

자체 비밀번호 인코더 세트를 정의할 수 있지만 PasswordEncoderFactories 에서 제공되는 기본 인코더 를 사용하는 것이 좋습니다 .

Spring Security 버전 5.7.0-M2부터 Spring  은 WebSecurityConfigureAdapter의 사용을 더 이상 사용하지 않으며  WebSecurityConfigureAdapter 없이 구성을 생성할 것을 제안합니다. 이  기사에서는 이에  대해 자세히 설명합니다.

3.2. NoOpPassword인코더

어떤 이유로든 구성된 비밀번호를 인코딩하지 않으려면 NoOpPasswordEncoder 를 사용할 수 있습니다 .

이렇게 하려면 {noop} 식별자 를 사용하여 password() 메서드에 제공하는 암호 문구에 접두사를 붙이기만 하면 됩니다.

@Configuration
public class InMemoryNoOpAuthWebSecurityConfigurer {

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.withUsername("spring")
            .password("{noop}secret")
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
}

이렇게 하면 Spring Security는 사용자가 제공한 비밀번호와 위에서 구성한 비밀번호를 비교할 때 후드 아래에서 NoOpPasswordEncoder 를 사용합니다.

그러나 프로덕션 애플리케이션에서는 이 접근 방식을 사용해서는 안 됩니다. 공식 문서에서 알 수 있듯이 NoOpPasswordEncoder 는 레거시 구현임을 나타내기 위해 더 이상 사용되지 않으며 이를 사용하는 것은 안전하지 않은 것으로 간주 됩니다 .

3.3. 기존 암호 마이그레이션

다음과 같이 기존 비밀번호를 권장되는 Spring Security 5 표준으로 업데이트할 수 있습니다.

  • 암호화된 값으로 저장된 일반 텍스트 암호 업데이트:
String encoded = new BCryptPasswordEncoder().encode(plainTextPassword);
  • 알려진 인코더 식별자로 해시된 저장된 비밀번호 접두사 추가:
{bcrypt}$2a$10$MF7hYnWLeLT66gNccBgxaONZHbrSMjlUofkp50sSpBw2PJjUqU.zS
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
  • 저장된 암호의 인코딩 메커니즘을 알 수 없는 경우 사용자에게 암호 업데이트 요청

4. 결론

이 빠른 예제에서는 새로운 암호 저장 메커니즘을 사용하여 유효한 Spring 4 메모리 내 인증 구성을 Spring 5로 업데이트했습니다.

항상 그렇듯이 GitHub 프로젝트 에서 소스 코드를 찾을 수 있습니다 .

Security footer banner