2

I'm trying to use the BCrypt password hashing in my Grails app that's using the Spring Security plugin. I've enabled BCrypt by adding the following to Config.groovy

grails.plugins.springsecurity.password.algorithm = 'bcrypt'

And I've defined the following codec to simplify using BCrypt to encode paswords:

public class PasswordCodec {

    // it doesn't seem to be possible to dependency-inject codecs, so lookup the bean ourselves
    @Lazy
    private static PasswordEncoder passwordEncoder = Holders.grailsApplication.mainContext.getBean('passwordEncoder')

    static encode = { str ->
        passwordEncoder.encodePassword(str.toString(), null)
    }
}

When I start the application in dev mode the database is bootstrapped with a few accounts (each of which has the same password, e.g.

3.times { i ->
    def username = "user$i"
    def password = "secret".encodeAsPassword()

    new User(username: username, password: password).save()
    // also assign the user a role
}

If I look in the database, I see that the encoded value of each of these user's password is different! So it's no surprise that when a user attempts to login and enters a password of "secret", the BCrypt-encoded password value doesn't match what's saved in the database, because it seems that the BCrypt-encoded value of a String somehow changes over time.

Obviously I'm doing something wrong here, but I've no idea what?

Dónal
  • 185,044
  • 174
  • 569
  • 824
  • I assume that your User domain doesn't have the typical s2 quickstart code that handles password encoding? – Joshua Moore Apr 15 '14 at 17:23
  • Also, I think salting is enabled by default now, which is based on username and thus would be different salts for each password: http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/passwords.html#salt ... assuming you still have the generated s2 quickstart code in place. – Joshua Moore Apr 15 '14 at 17:37
  • @JoshuaMoore no, I don't do any password encoding in the User class itself (I didn't use s2-quickstart) – Dónal Apr 15 '14 at 17:40
  • @JoshuaMoore I encode the password myself in `PasswordCodec` and pass `null` for the salt parameter – Dónal Apr 15 '14 at 17:42
  • Completely odd then. Perhaps Burt will pop by and offer some assistance. I don't see anything obviously wrong with what you are doing and it mimics what we use to have to do in Spring ages ago. – Joshua Moore Apr 15 '14 at 17:49

1 Answers1

9

Often password hashes are identical given the same input (and very occasionally with different inputs). But bcrypt and others generate a different hash each time. This isn't a problem however, because the PasswordEncoder interface has both the String encodePassword(String rawPass, Object salt) method to generate hashes, but also the boolean isPasswordValid(String encPass, String rawPass, Object salt) method to validate them.

With simpler hashes like MD5, SHA-1, etc. the process for verification is simply re-encoding the cleartext password and checking that it's the same as the stored hashed value. The process is a lot more complicated with bcrypt, but the end result is the same - it doesn't check for equality, but instead that they're equivalent. So if you hash the same password twice and compare them with isPasswordValid it will return true.

Burt Beckwith
  • 75,342
  • 5
  • 143
  • 156
  • How is this deprecated PasswordEncoder different from this [PasswordEncoder](http://docs.spring.io/spring-security/site/docs/3.2.x/apidocs/org/springframework/security/crypto/password/PasswordEncoder.html), other than providing a salt by user in the former case? (Out of context of question) – dmahapatro Apr 15 '14 at 18:11
  • 1
    I haven't looked closely at the differences, but I'm planning on migrating to the newer interface. – Burt Beckwith Apr 15 '14 at 19:09
  • 1
    @BurtBeckwith I completely missed the `isPasswordValid` method, thanks a lot – Dónal Apr 15 '14 at 19:39