1. 序章

最新のSpringSecurityリリースでは、多くの変更が加えられました。 これらの変更の1つは、アプリケーションでパスワードエンコーディングを処理する方法です。

このチュートリアルでは、これらの変更のいくつかを調べます。

後で、ユーザーが認識しないように、新しい委任メカニズムを構成する方法と、既存のパスワードエンコーディングを更新する方法を説明します。

2. SpringSecurity5.xの関連する変更

Spring Securityチームは、org.springframework.security.authentication.encodingPasswordEncoderを非推奨として宣言しました。 古いインターフェースはランダムに生成されたソルト用に設計されていなかったため、これは論理的な動きでした。 その結果、バージョン5ではこのインターフェイスが削除されました。

さらに、Spring Securityは、エンコードされたパスワードの処理方法を変更します。 以前のバージョンでは、各アプリケーションは1つのパスワードエンコーディングアルゴリズムのみを採用していました。

デフォルトでは、StandardPasswordEncoderはそれを処理します。 エンコーディングにはSHA-256を使用しました。 パスワードエンコーダーを変更することで、別のアルゴリズムに切り替えることができます。 しかし、私たちのアプリケーションは、正確に1つのアルゴリズムに固執する必要がありました。

バージョン5.0では、パスワードエンコーディングの委任の概念が導入されています。これで、パスワードごとに異なるエンコーディングを使用できるようになりました。 Springは、エンコードされたパスワードの前に付けられた識別子によってアルゴリズムを認識します。

bcryptでエンコードされたパスワードの例を次に示します。

{bcrypt}$2b$12$FaLabMRystU4MLAasNOKb.HUElBAabuQdX59RWHq5X.9Ghm692NEi

bcryptが最初に中括弧で指定されていることに注意してください。

3. 委任構成

パスワードハッシュにプレフィックスがない場合、委任プロセスはデフォルトのエンコーダーを使用します。したがって、デフォルトでは、StandardPasswordEncoder。を取得します。

これにより、以前のSpringSecurityバージョンのデフォルト構成と互換性があります。

バージョン5では、SpringSecurityはPasswordEncoderFactories.createDelegatingPasswordEncoder()。を導入します。このファクトリメソッドは、DelegationPasswordEncoderの構成済みインスタンスを返します。

プレフィックスのないパスワードの場合、そのインスタンスは上記のデフォルトの動作を保証します。 また、プレフィックスを含むパスワードハッシュの場合、委任はそれに応じて行われます。

Spring Securityチームは、サポートされているアルゴリズムを対応するJavaDocの最新バージョンにリストしています。

もちろん、Springではこの動作を構成できます。

サポートしたいとします。

  • 新しいデフォルトとしてのbcrypt
  • 代替としてscrypt
  • 現在使用されているアルゴリズムとしてのSHA-256。

このセットアップの構成は次のようになります。

@Bean
public PasswordEncoder delegatingPasswordEncoder() {
    PasswordEncoder defaultEncoder = new StandardPasswordEncoder();
    Map<String, PasswordEncoder> encoders = new HashMap<>();
    encoders.put("bcrypt", new BCryptPasswordEncoder());
    encoders.put("scrypt", new SCryptPasswordEncoder());

    DelegatingPasswordEncoder passworEncoder = new DelegatingPasswordEncoder(
      "bcrypt", encoders);
    passworEncoder.setDefaultPasswordEncoderForMatches(defaultEncoder);

    return passworEncoder;
}

4. パスワードエンコーディングアルゴリズムの移行

前のセクションでは、必要に応じてパスワードエンコーディングを構成する方法について説明しました。 したがって、ここでは、すでにエンコードされているパスワードを新しいアルゴリズムに切り替える方法に取り組みます。

エンコーディングをSHA-256からbcryptに変更したいとしますが、ユーザーがパスワードを変更することは望ましくありません。

考えられる解決策の1つは、ログイン要求を使用することです。 この時点で、プレーンテキストの資格情報にアクセスできます。 これが、現在のパスワードを取得して再エンコードできる瞬間です。

したがって、そのためにSpringのAuthenticationSuccessEventを使用できます。 このイベントは、ユーザーがアプリケーションに正常にログインした後に発生します。

サンプルコードは次のとおりです。

@Bean
public ApplicationListener<AuthenticationSuccessEvent>
  authenticationSuccessListener( PasswordEncoder encoder) {
    return (AuthenticationSuccessEvent event) -> {
        Authentication auth = event.getAuthentication();

        if (auth instanceof UsernamePasswordAuthenticationToken
          && auth.getCredentials() != null) {

            CharSequence clearTextPass = (CharSequence) auth.getCredentials();
            String newPasswordHash = encoder.encode(clearTextPass);

            // [...] Update user's password

            ((UsernamePasswordAuthenticationToken) auth).eraseCredentials();
        }
    };
}

前のスニペット:

  • 提供された認証の詳細からクリアテキストでユーザーパスワードを取得しました
  • 新しいアルゴリズムで新しいパスワードハッシュを作成しました
  • 認証トークンからクリアテキストのパスワードを削除しました

デフォルトでは、Springセキュリティがパスワードをできるだけ早く削除するため、クリアテキストでパスワードを抽出することはできません。

したがって、パスワードのクリアテキストバージョンを保持するようにSpringを構成する必要があります。

さらに、エンコーディング委任を登録する必要があります。

@Configuration
public class PasswordStorageWebSecurityConfigurer
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
      throws Exception {
        auth.eraseCredentials(false)
          .passwordEncoder(delegatingPasswordEncoder());
    }

    // ...
}

5. 結論

この簡単な記事では、5.xで利用できるいくつかの新しいパスワードエンコーディング機能について説明しました。

また、パスワードをエンコードするために複数のパスワードエンコードアルゴリズムを構成する方法も確認しました。 さらに、既存のパスワードを壊すことなく、パスワードのエンコーディングを変更する方法を模索しました。

最後に、Springイベントを使用して、暗号化されたユーザーパスワードを透過的に更新し、ユーザーに開示せずにエンコード戦略をシームレスに変更できるようにする方法について説明しました。

最後に、いつものように、すべてのコード例はGitHubリポジトリで入手できます。