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:
I just noticed `upgradeEncoding(java.lang.String encodedPassword)`on Spring Security's PasswordEncoder. Nice addition, @rob_winch !
— Michael Simons (@rotnroll666) August 15, 2019
No comments yet
One Trackback/Pingback
[…] >> Spring Security 5.1: Upgrade / Rehash Stored Passwords [info.michael-simons.eu] […]
Post a Comment