Spring Security 5.1: Upgrade / Rehash stored passwords

August 15, 2019 by Michael

In this post from early 2018, I described the new password infrastructure of Spring Security 5.0. As the post got some feedback the last couple of days, I jumped back a bit into Spring Security code and noticed this method here: PasswordEncoder#upgradeEncoding. Hm, what does it do?

It turns out, it works together with the additional, new interface UserDetailsPasswordService respectively ReactiveUserDetailsPasswordService.

TL;DR version: The delegating password encoder checks by default, whether the password hash’s id is present and matches the id to encode (see linked post above). If so, all is fine. Otherwise, the password needs an upgrade.

In my example application, I used an in-memory user details service with a user “michael” and password “SIMONS”, stored as a ROT-13 “hash” (I’m still waiting to see BSPasswordEncoder in production somewhere). The hash has of course no prefix. While I of course never used a password encoder like this, I migrated several applications hashed passwords from sha1 to bcrypt and pbkdf2 with a similar mechanism.

With the lastest commit I could remove all my custom ceremony for an upgrade. (see Upgrade to Spring Boot 2.1.7 and rely on PasswordEncoder#upgradeEncoding.). As the InMemoryUserDetailsManager is also a UserDetailsPasswordService, Spring Boot wires everything smooth and nicely for me.

The InMemoryUserDetailsManager of course just changes the in-memory hash.

A fictive, JDBCTemplate-based UserDetailsPasswordService could look like this:

public class JdbcUserDetailsPasswordService implements UserDetailsPasswordService {
    private final UserDetailsService userDetailsService;
 
    private final JdbcTemplate jdbcTemplate;
 
    public JdbcUserDetailsPasswordService(UserDetailsService userDetailsService, JdbcTemplate jdbcTemplate) {
        this.userDetailsService = userDetailsService;
        this.jdbcTemplate = jdbcTemplate;
    }
 
    @Override
    public UserDetails updatePassword(UserDetails user, String newPassword) {
        jdbcTemplate.update("UPDATE users SET hashed_password = ? WHERE username = ?", newPassword, user.getUsername());
        return userDetailsService.loadUserByUsername(user.getUsername()); // reload the whole user details
    }
}

The most important thing here is the fact, that UserDetailsPasswordService#updatePassword actually get’s the newly hashed password! A clear separation of concern, the update mechanism doesn’t need to know anything about the new hash that is used, it just has to store the string version of it! Apart from that, my version here reloads the complete UserDetails from a UserDetailsService not shown here. The implementation would be JDBCTemplate-based, too, in this case.

I really like this addition:

No comments yet

Post a Comment

Your email is never published. We need your name and email address only for verifying a legitimate comment. For more information, a copy of your saved data or a request to delete any data under this address, please send a short notice to michael@simons.ac from the address you used to comment on this entry.
By entering and submitting a comment, wether with or without name or email address, you'll agree that all data you have entered including your IP address will be checked and stored for a limited time by Automattic Inc., 60 29th Street #343, San Francisco, CA 94110-4929, USA. only for the purpose of avoiding spam. You can deny further storage of your data by sending an email to support@wordpress.com, with subject “Deletion of Data stored by Akismet”.
Required fields are marked *