Spring Security 5: New password storage format

With Spring Boot 2 comes Spring Security 5. Learn about a new password storage format
January 13, 2018 by Michael

Spring Security 5 has been out for some days now. People on Spring Boot 1 or plain Spring must manually upgrade their dependencies at the moment to notice the new kid on the block. But with Spring Boot 2 Spring Security 5 will be the default for new applications but also for applications that are migrated.

For me as the author of the upcoming Spring Boot Buch (Which you can preorder here) the migration turned out to be pretty hard (hard as in “I had to rewrite lots of examples and text”). The Spring Boot team changed a lot of the “magic” defaults in regard of security and also Actuator security. I’m not saying I don’t like the changes, quite the contrary actually, but it just took some time.

In this post I’m gonna explain the changes in the way password encoding works in Spring Security 5. Most certainly your application will be affected in some way if you upgrade to Spring Boot 2. I will explain migration scenarios if you already have secure hashes or none at all, but also an interesting way of converting insecure hashes to secure hashes and not only migrating them in the sense that they are usable with Spring Security 5 again.

Modernized Password encoding

Whether a password hash is secure or not is in constant flux. There was a time way back then when even a MD5-Hash was regarded secure. Long gone. SHA-1 and SHA-256: Insecure. Either there have been collisions or huge enough rainbow tables, it’s a constant “battle”. In Spring Security those things have been dealt with through a concept of PasswordEncoder. Historically it came from the org.springframework.security.authentication.encoding. The package from that interface has been deprecated for some time now and it’s finally gone from Spring Security 5. A good thing, as “encryption” does not depend on “authentication”. I really wish other frameworks and especially applications would do this kind of housekeeping more often.

So that’s the first thing taking in regard when upgrading to Boot 2: Those encoders are gone. If you’re user details system depends on something like the old ShaPasswordEncoder or Md5PasswordEncoder, you have to do an active migration. The algorithms are still available, but adapted to the current PasswordEncoder-interface residing in org.springframework.security.crypto.password. However, they are all deprecated to mark them as insecure.

A framework like Spring Security can only introduce breaking changes like this once in while, so the team behind Rob Winch made that one count.

What is a good, new password encoder for replacing the old default NoOpPasswordEncoder? You’d say BCryptPasswordEncoder? Nope, not really. That would tie everything again to a specific implementation. Spring Security 5 defaults now to DelegatingPasswordEncoder.

Delegating means being able to use multiple implementations to

  • encode new passwords
  • validate passwords in old and new formats
  • change encoding in the future

The encoder introduces a new password storage format for that purpose. It looks like {id}encodedPassword where {id} represents a logical name of a specific encoder and encodedPassword the actual hash.

The delegating encoder implements the following logic for matching:

  • If there is a known ID for an encoder, use that one to verify the password
  • If there is an encoder configured for having no id, use that, otherwise throw an exception

And for encoding: New passwords are encoded with the encoder identified with a parameter to the constructor of the delegating encoder.

That means you have to do an active migration in most situations.

Migration scenarios

You have been relying on the old default NoOpPasswordEncoder

That means that your passwords are stored in plain text. A bad situation in most cases. Creating an instance of NoOpPasswordEncoder and thus replacing the default delegating encoder allows you to postpone migrations of your passwords to a later date. The next better solution would be prefixing all existing passwords with {noop} and keeping the default encoder of Spring Security 5.

Best option in this case is actually hashing all existing passwords in a a batch run (i.e. in the most simple form of an ApplicationRunner instance) by using the default encoder instance like this:

String encoded = passwordEncoder.encode(plainTextPassword);

You’ll end up with bcrypt-hashed passwords.

Your passwords are already encoded (without a salt source)

Two options here: By all probability you are aware which hash you’re using and are able to update your hashed passwords to include the prefix.

If you you instantiate your password encoder like this

PasswordEncoderFactories.createDelegatingPasswordEncoder();

you get encoders for those prefixes as of Spring Security 5.0:

Map<String, PasswordEncoder> encoders = new HashMap<>();
String encodingId = "bcrypt";
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new LdapShaPasswordEncoder());
encoders.put("MD4", new Md4PasswordEncoder());
encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new StandardPasswordEncoder());

that means you can prefix your hashed passwords like {SHA-256}encodedPassword and you’re okayish to go. Okayish because you are now able to live on with insecure hashes. Nice thing here is that you can achieve by just updating all passwords at once, for example just using an SQL-query.

The Second option instead of migrating without touching the passwords themselves is this:

PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
passwordEncoder.setDefaultPasswordEncoderForMatches(new MessageDigestPasswordEncoder("SHA-256"));

This way you won’t have to update your passwords as the delegating encoder has a fallback. New passwords would be encoded with bcrypt then.

That leads me to the third scenario

Rehashing and upgrading passwords

To rehash an existing password one has to know the plain text as all those hashes are one way functions. You just can invalide all accounts and force all users to change their password next time they login or you can do this automatically. If you want to do this automatically, you the the clear text password. The one and only moment in time to retrieve that is when a user authenticates against your system.

A word of warning: This can be insecure and normally, Spring Security prevents the credentials of a username and password authentication token dangling around.

Following is a migration scenario from a real bullsh*t encoder to a secure system. The author of the encoder was so bad that he even didn’t realize that he has been using a reversible hash all the time! (I kid you not, I actually have seen things like this in the not so distant past! (and no, it wasn’t me)).

The following example code is part of this repository michael-simons/passwordmigration. All beans are configured in the nested SecurityConfig class. Passwords have been encoded with an encoder called BSPasswordEncoder for a reason. The author realized that hashing passwords this way is very, very bad and wants to update them. He configures the delegating password encoder like this:

Please read through the comments, they are part of the story!

The current user and password repository comes from a database, but imagine for a second it’s something like this, so you can see the insecure password hash (you probably can rehash them without cleartext, can’t you?):

That user details service can actually be anything, it just illustrates stuff here. Next the system has to be hooked to Spring Securities event system, which fires successful and failed logins:

You than have to instantiate a WebSecurityConfigurerAdapter that connects the dots. Be careful: With Spring Boot 2.0 this turns Boots magic in regard to security off and all defaults come from Spring Security itself:

The last missing bit is a listener that is notified when a user logs in. It can look like this:

Summing this up: This is a scenario that shouldn’t be live for all the time in the world. I used it in my private project Daily Fratze around 2009 when I migrated all old SHA-1 hashes to BCrypt. It takes some time until all users have been logged in at least once. Maybe you don’t even get them all, but then you can still reset their passwords.

Update

Since Spring Security 5.1, the delegating password encoder automatically detects, whether an upgrade is necessary and the above code can be drastically shortened, see this commit: https://github.com/michael-simons/passwordmigration/commit/0d8ff58a0f1777417e2407366f3884b2051d1335.

(Featured image on this post by portal gda.)

14 comments

  1. Wim Deblauwe wrote:

    Hi,

    FYI: the code in the last section is not displaying (Rehashing and upgrading passwords).

    regards,

    Wim

    Posted on May 13, 2018 at 2:49 PM | Permalink
  2. Michael wrote:

    Thanks Wim, I’ll check…

    Posted on May 13, 2018 at 4:40 PM | Permalink
  3. Norbertas Gaulia wrote:

    wow, now db can contain any type of encoded passwords +your own if needed, that’s more magic than it was. Quick solution for migration could be rewriting getPassword() on User entity: return “{sha-256}”+password; ?

    Posted on May 31, 2018 at 4:25 PM | Permalink
  4. Anatoly Sapunov wrote:

    Thank you very much for the article!
    It helped a lot.

    Posted on July 17, 2018 at 2:14 PM | Permalink
  5. Michael wrote:

    Thank you for that kind feedback! Glad it helped.

    Posted on July 18, 2018 at 7:36 PM | Permalink
  6. rodrigo wrote:

    Thank you very much for the article!
    I use MD5 with base64, how to force base64 in setDefaultPasswordEncoderForMatches (new MessageDigestPasswordEncoder (“MD5”))?

    Posted on October 31, 2018 at 8:29 PM | Permalink
  7. Hans wrote:

    Hi Michael,
    I am a little desperate , we have a PostgreSQL 9.1 with chkpass encoded passwords. Now I develop a modern SpringBoot (atm 1.5.22) WebUI and using the old app jar as library. How can I validate with Spring Security against these old chkpass passwords ?

    Thanks alot Hans

    Posted on August 11, 2019 at 8:40 AM | Permalink
  8. Michael wrote:

    Hans, I don’t understand what you mean by “using the old app jar as library…”

    I’d extend from

    AbstractPasswordEncoder

    (see

    https://docs.spring.io/spring-security/site/docs/5.1.6.RELEASE/api/org/springframework/security/crypto/password/package-summary.html

    ) and do my chkpass implementation there.

    Then of course configure my custom encoder accordingly.

    Posted on August 14, 2019 at 9:05 AM | Permalink
  9. Hans wrote:

    Hi Michael,
    I include our GPS Tracker jar as a dependency in my Maven project. So I get all the business logic from our client app also in Spring Boot running a REST API on a server to be consumed by an Angular frontend.
    The approach for the chkpass passwords is that you need username (or userId) and password and check if :
    SELECT count(*) from person where username = :username and password = :password
    is greater zero. I have seen that the custom emcoders compare only rawPassword against encodedPassword.
    How could I get username or userId in the encoder ? Via SecurityContext ?
    Thx alot Hans

    Posted on August 15, 2019 at 6:32 AM | Permalink
  10. Michael wrote:

    That is a two step process with Spring Security. You would need to implement a org.springframework.security.core.userdetails.UserDetailsService and select your user details based on the user name in public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException.

    That returns a UserDetails, contains the – hopefully hashed – password.

    Spring Security’s infrastructure will than use matches automatically for you.

    In short: No, you cannot select the user by username and password.

    Here is a full example https://github.com/springbootbuch/security that shows various approaches.

    Posted on August 15, 2019 at 8:38 PM | Permalink
  11. Michael wrote:

    I just noticed that the gists in my blog haven’t been visible to some https issues. I fixed this, I hope things are clearer now.

    Posted on August 15, 2019 at 9:52 PM | Permalink
  12. hans wrote:

    Hi Michael,

    I used this github project as basis for my SB project:

    https://github.com/mrin9/Angular-SpringBoot-REST-JWT

    , where the author already uses a SecurityConfig.java, but only to authenticate against a H2 in-memory database with plain text passwords, so he obviously bypasses the diffcult parts 🙂 When I configure a postgres driver in application.properties and pom.xml, I used a simple matches funtionality like in NoOpPasswordEncoder, but this doesn’t work.
    This is where my SpringSecurity knowledge ends, how to tell the encoder, that he has to match chkpass encoded passwords against a plain text password entry by the user. Could you show a PSQL example for this demo project ?

    Thx alot Hans

    Posted on August 16, 2019 at 10:55 AM | Permalink
  13. Michael wrote:

    Please read about Spring Security’s architecture:
    https://docs.spring.io/spring-security/site/docs/5.1.6.RELEASE/reference/htmlsingle/#overall-architecture

    and especially UserDetailsService
    https://docs.spring.io/spring-security/site/docs/5.1.6.RELEASE/reference/htmlsingle/#userdetailsservice-implementations
    and password encoding:

    https://docs.spring.io/spring-security/site/docs/5.1.6.RELEASE/reference/htmlsingle/#core-services-password-encoding

    Posted on August 16, 2019 at 11:03 AM | Permalink
  14. Ariful Islam Arif wrote:

    “Your passwords are already encoded (without a salt source)” So if my password is with salt source then i am done. Is there any other way.

    Posted on March 8, 2020 at 8:51 AM | Permalink
4 Trackbacks/Pingbacks
  1. […] Blogpost Spring Security 5: A new password storage format erklärt die Vorteile im Detail und zeigt einige Möglichkeiten zur Migration […]

  2. […] 内部キャッシュ用にShaPasswordEncoderを使っていましたが、クラス自体がなくなりました。 PasswordEncoder関連のクラス構成が見直されています。それに合わせて、脆弱なアルゴリズムを使ったShaPasswordEncoderクラスやMd5PasswordEncoderクラスは姿を消しています(アルゴリズム自体は残っているようですが、非推奨とのこと)。 […]

  3. […] this post from early 2018, I described the new password infrastructure of Spring Security 5.0. As the post got some feedback […]

  4. […]   使用我就不介绍了,有兴趣可以自己探索一波,还可以参考一下文章一、文章二,下面我粘贴我的代码,讲到的这些可以扩展的地方大家我在代码中会标识清楚,喜欢动手可以尝试一下,我的重点是OAuth2验证; […]

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 *