Novo armazenamento de senha no Spring Security 5

Novo armazenamento de senha no Spring Security 5

1. Introdução

Com o último lançamento do Spring Security, muita coisa mudou. Uma dessas mudanças é como podemos lidar com a codificação de senha em nossos aplicativos.

Neste tutorial, vamos explorar algumas dessas mudanças.

Posteriormente, veremos como configurar o novo mecanismo de delegação e como atualizar nossa codificação de senha existente, sem que nossos usuários reconheçam.

2. Mudanças relevantes no Spring Security 5.x

A equipe de Spring Security declarouPasswordEncoder emorg.springframework.security.authentication.encoding como obsoleto. Foi uma jogada lógica, já que a interface antiga não foi projetada para um sal gerado aleatoriamente. Conseqüentemente, a versão 5 removeu essa interface.

Além disso, o Spring Security altera a maneira como lida com senhas codificadas. Nas versões anteriores, cada aplicativo empregava apenas um algoritmo de codificação de senha.

Por padrão,StandardPasswordEncoder lidou com isso. Ele usou SHA-256 para a codificação. Alterando o codificador de senha, podemos mudar para outro algoritmo. Mas nosso aplicativo teve que seguir exatamente um algoritmo.

Version 5.0 introduces the concept of password encoding delegation. Agora, podemos usar codificações diferentes para senhas diferentes. O Spring reconhece o algoritmo por um identificador prefixando a senha codificada.

Aqui está um exemplo de senha codificada com bcrypt:

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

Observe como o bcrypt é especificado entre chaves no início.

3. Configuração de Delegação

If the password hash has no prefix, the delegation process uses a default encoder. Portanto, por padrão, obtemos oStandardPasswordEncoder.

Isso o torna compatível com a configuração padrão das versões anteriores do Spring Security.

Com a versão 5, Spring Security introduzPasswordEncoderFactories.createDelegatingPasswordEncoder(). Este método de fábrica retorna uma instância configurada deDelegationPasswordEncoder.

Para senhas sem prefixo, essa instância garante o comportamento padrão mencionado acima. E para hashes de senha que contêm um prefixo, a delegação é feita de acordo.

A equipe do Spring Security lista os algoritmos com suporte na versão mais recente docorresponding JavaDoc.

Obviamente, o Spring nos permite configurar esse comportamento.

Vamos supor que queremos apoiar:

  • bcrypt como nosso novo padrão

  • scrypt como alternativa

  • SHA-256 como o algoritmo usado atualmente.

A configuração para esta instalação terá a seguinte aparência:

@Bean
public PasswordEncoder delegatingPasswordEncoder() {
    PasswordEncoder defaultEncoder = new StandardPasswordEncoder();
    Map 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. Migrando o Algoritmo de Codificação de Senha

Na seção anterior, exploramos como configurar a codificação de senha de acordo com nossas necessidades. Therefore, now we’ll work on how to switch an already encoded password to a new algorithm.

Vamos imaginar que queremos mudar a codificação deSHA-256 parabcrypt, no entanto, não queremos que nosso usuário mude suas senhas.

Uma solução possível é usar a solicitação de login. Neste ponto, podemos acessar as credenciais em texto sem formatação. Nesse momento, podemos pegar a senha atual e recodificá-la.

Consequentemente, podemos usarAuthenticationSuccessEvent do Spring para isso. Este evento é disparado depois que um usuário faz login com sucesso em nosso aplicativo.

Aqui está o código de exemplo:

@Bean
public ApplicationListener
  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();
        }
    };
}

No trecho anterior:

  • Recuperamos a senha do usuário em texto não criptografado a partir dos detalhes de autenticação fornecidos

  • Criou um novo hash de senha com o novo algoritmo

  • Removida a senha de texto não criptografado do token de autenticação

Por padrão, extrair a senha em texto não criptografado não seria possível porque o Spring Security a exclui o mais rápido possível.

Portanto, precisamos configurar o Spring para manter a versão em texto não criptografado da senha.

Além disso, precisamos registrar nossa delegação de codificação:

@Configuration
public class PasswordStorageWebSecurityConfigurer
  extends WebSecurityConfigurerAdapter {

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

    // ...
}

5. Conclusão

Neste artigo rápido, falamos sobre alguns novos recursos de codificação de senha disponíveis no 5.x.

Também vimos como configurar vários algoritmos de codificação de senha para codificar nossas senhas. Além disso, exploramos uma maneira de alterar a codificação de senha, sem quebrar a existente.

Por fim, descrevemos como usar os eventos do Spring para atualizar a senha de usuário criptografada de forma transparente, permitindo alterar perfeitamente nossa estratégia de codificação sem divulgá-la a nossos usuários.

Por último e como sempre, todos os exemplos de código estão disponíveis em nossoGitHub repository.