PasswordEncoder con BCrypt y Argon2

Intermedio
Spring Boot
Spring Boot
Actualizado: 07/05/2026

Diagrama: tutorial-spring-security-password-encoder-bcrypt-argon2

Por que no usar MD5 ni SHA1

Los hashes criptograficos genéricos como MD5 y SHA1 estan pensados para ser rápidos. En contraseñas esa rapidez es una debilidad, porque permite a un atacante calcular miles de millones de hashes por segundo en GPUs. Spring Security recomienda algoritmos específicos de contraseña que incluyen salt aleatorio y factor de coste configurable: BCrypt, PBKDF2, SCrypt y Argon2.

BCrypt por defecto

BCryptPasswordEncoder es el encoder recomendado para la mayoria de aplicaciones. utiliza un salt interno y un factor de coste ajustable en potencias de 2.

@Bean
PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(12);
}

Un factor de coste 12 ofrece un buen equilibrio entre seguridad y rendimiento en 2026. El factor se debe ajustar al hardware del servidor, con tiempos objetivo de hashing entre 250 y 500 milisegundos.

Argon2 para máxima robustez

Argon2, ganador del Password Hashing Competition, es la recomendación actual del OWASP para nuevas aplicaciones. Spring Security expone Argon2PasswordEncoder.

@Bean
PasswordEncoder passwordEncoder() {
    return Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
}

Los parámetros por defecto son:

  • saltLength: 16 bytes.
  • hashLength: 32 bytes.
  • parallelism: 1.
  • memory: 16384 kibibytes.
  • iterations: 2.

Argon2 es costoso en memoria, lo que dificulta ataques con GPU. Asegurate de probar el tamano de memoria en tu entorno de producción para evitar latencias excesivas.

DelegatingPasswordEncoder para rotación

PasswordEncoderFactories.createDelegatingPasswordEncoder() construye un encoder que reconoce varios algoritmos y almacena el prefijo en el hash:

{bcrypt}$2a$12$...
{argon2}$argon2id$v=19$m=16384,t=2,p=1$...

Al verificar una contraseña, Spring Security inspecciona el prefijo y usa el encoder correspondiente. Al crear nuevas contraseñas, emplea el encoder por defecto. Este mecanismo permite migrar usuarios existentes de forma gradual: se detecta un algoritmo antiguo en el login, se valida con el encoder viejo y se rehashea con el nuevo durante la sesión.

Integración con DaoAuthenticationProvider

Para que todo funcione extremo a extremo, el DaoAuthenticationProvider debe recibir el mismo PasswordEncoder y el UserDetailsService debe almacenar contraseñas ya codificadas (nunca en texto plano). Cualquier desalineación provoca errores de autenticación silenciosos que son difíciles de depurar.

Ejemplo completo de registro y login

Un flujo de registro típico hashea la contraseña antes de persistir el usuario en la base de datos. El login posterior compara el hash almacenado con la contraseña recibida sin volver a codificarla manualmente.

@Service
public class RegistroService {

    private final AppUserRepository repo;
    private final PasswordEncoder encoder;

    public RegistroService(AppUserRepository repo, PasswordEncoder encoder) {
        this.repo = repo;
        this.encoder = encoder;
    }

    public AppUser registrar(String username, String rawPassword, Role role) {
        if (repo.existsByUsername(username)) {
            throw new UsernameYaExisteException(username);
        }
        String hash = encoder.encode(rawPassword);
        return repo.save(new AppUser(username, hash, role));
    }
}

@Configuration
public class PasswordConfig {

    @Bean
    PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

El DaoAuthenticationProvider se encarga de comparar internamente el hash almacenado con la contraseña recibida mediante encoder.matches(raw, hash), sin que el desarrollador tenga que invocarlo manualmente.

Elección de algoritmo y factor

Los valores de referencia del OWASP Cheat Sheet en 2026 son: BCrypt cost 12 o 13, Argon2id con 19 MiB de memoria y 2 iteraciones como mínimo, PBKDF2-HMAC-SHA256 con 600 000 iteraciones.

Rotación progresiva en proyectos legacy

En carteras corporativas antiguas es frecuente encontrar hashes MD5 o SHA1 sin salt. DelegatingPasswordEncoder permite soportarlos durante la migración sin forzar un reset global de contraseñas:

@Bean
PasswordEncoder delegatingEncoder() {
    Map<String, PasswordEncoder> encoders = Map.of(
        "bcrypt", new BCryptPasswordEncoder(12),
        "argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8(),
        "pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8(),
        "noop",   NoOpPasswordEncoder.getInstance() // solo para migrar, nunca produccion
    );
    DelegatingPasswordEncoder delegating = new DelegatingPasswordEncoder("argon2", encoders);
    delegating.setDefaultPasswordEncoderForMatches(new BCryptPasswordEncoder(12));
    return delegating;
}

Al autenticar con exito, implementa un listener que rehashea la contraseña con el algoritmo por defecto (argon2) y la actualiza en la base de datos, completando la rotación sin interacción del usuario.

Casos de uso B2B

  • Banca y fintech: Argon2id por recomendación OWASP y requisitos regulatorios PSD3; auditoría interna exige justificar los parámetros elegidos.
  • Telco y ecommerce: BCrypt cost 12 como valor por defecto, con DelegatingPasswordEncoder si hay hashes previos importados de sistemas legacy.
  • Sector público: alineación con el ENS (Esquema Nacional de Seguridad) que exige algoritmos con salt único por usuario y factor de coste ajustable.
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Spring Boot es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Spring Boot

Explora más contenido relacionado con Spring Boot y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Seleccionar un algoritmo de hashing apropiado para contraseñas. Configurar BCryptPasswordEncoder y Argon2PasswordEncoder. Usar DelegatingPasswordEncoder para convivir y rotar varios algoritmos.