I'm a bit new to play framework and password hashing. I tried to find some solutions for hashing my passwords and I found BCrypt. Do you think that's good enough to hashing passwords. And if it's good, how can I get it working in the play framework? (I'm using play 2.1.3) Thanks!
BCrypt is a password hashing algorithm, designed with all security precautions we've mentioned in mind. It is used as the default password hashing algorithm in OpenBSD, an open-source security-focused operating system, and is the most widely supported hashing algorithm to date.
itself gives clues to hacker(i.e, who hacked the DB) that the encryption is done using Bcrypt algorithm and so he can easily hack by using password_verify() in php by passing parameters as his guess passwords. I wouldn't say 'easily': brute forcing Bcrypt will take a very, very long time. Yes, it is fine to do so.
Here's a sample Play Java project I wrote that uses BCrypt to hash passwords, see the newUser() and signIn() actions:
https://github.com/jroper/play-demo-twitbookplus/blob/master/app/controllers/UserController.java
You can do similar in Scala. To summarise, add jbycrpt to your dependencies in Build.scala:
val appDependencies = Seq( "org.mindrot" % "jbcrypt" % "0.3m" )
Then hash passwords using this:
String passwordHash = BCrypt.hashpw(password, BCrypt.gensalt());
And verify passwords using this:
BCrypt.checkpw(password, passwordHash)
Update (2020)
In my projects these days, I no longer use BCrypt, rather I use PBKDF2 hashes, which has the advantage of not requiring any additional dependencies, but the disadvantage of needing to write a fair bit more code and manually manage the salt. BCrypt also has some issues where different implementations are unable accurately to consume each others output, and some implementations even truncate long passwords, which is really bad. Even though it's a lot more code, I like this approach because it gives me more control, and it shows transparently exactly how things are working, and makes it easy to update things over time since the recommendations for hashing algorithms and input parameters continually change.
Anyway, here's the code I use, it stores the salt and the number of iterations used (so that these can be increased over time as best practices recommend) in the hashed value, separated by colons:
val DefaultIterations = 10000 val random = new SecureRandom() private def pbkdf2(password: String, salt: Array[Byte], iterations: Int): Array[Byte] = { val keySpec = new PBEKeySpec(password.toCharArray, salt, iterations, 256) val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256") keyFactory.generateSecret(keySpec).getEncoded } def hashPassword(password: String, salt: Array[Byte]): String = { val salt = new Array[Byte](16) random.nextBytes(salt) val hash = pbkdf2(password, salt, DefaultIterations) val salt64 = Base64.getEncoder.encodeToString(salt) val hash64 = Base64.getEncoder.encodeToString(hash) s"$DefaultIterations:$hash64:$salt64" } def checkPassword(password: String, passwordHash: String): Boolean = { passwordHash.split(":") match { case Array(it, hash64, salt64) if it.forall(_.isDigit) => val hash = Base64.getDecoder.decode(hash64) val salt = Base64.getDecoder.decode(salt64) val calculatedHash = pbkdf2(password, salt, it.toInt) calculatedHash.sameElements(hash) case other => sys.error("Bad password hash") } }
My actual code is a little bit more complex, I include a versioned magic word as the first component (ph1:
), which means if I decided to change hashing algorithms or other input parameters that are not encoded in the output value, I can do that by encoding those hashes by updating the magic word to ph2:
, and then I can have code that validates both the old ph1
and new ph2
hashes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With