26

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!

biesior
  • 54,554
  • 10
  • 118
  • 177
Tigi1991
  • 301
  • 1
  • 4
  • 8

4 Answers4

50

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.

James Roper
  • 12,340
  • 41
  • 44
  • 1
    if BCrypt creates a hash using salt, you need to add a salt to the User model, don't you? There is no such a field for "salt" in your example. – アレックス Jan 08 '14 at 16:20
  • 5
    No, BCrypt includes the plain salt in the value it outputs. I'm not 100% sure of the format, but it would be something along the lines of "randomsalthere:saltedhashhere". So it just has to parse the value first, extract the salt and the hash out, and then it has both the hash and salt to do password checks. – James Roper Jan 08 '14 at 22:24
8

BCrypt is good for hashing passwords. Use Silhouette and use the BCrypt password hasher for your password plugin.

Will Sargent
  • 4,268
  • 1
  • 29
  • 50
5

This project offers a nice scala wrapper around jbcrypt https://github.com/t3hnar/scala-bcrypt - have used it in the past and it works great.

foomip
  • 504
  • 6
  • 7
0

Here are the instructions for the more recent bcrypt t3hnar library version 4.1 to work with the the Play Framework:

Add the dependency to build.sbt:

libraryDependencies += "com.github.t3hnar" %% "scala-bcrypt" % "4.1"

Add a Hash object to your project:

// Reference: https://github.com/t3hnar/scala-bcrypt

package utilities

import com.github.t3hnar.bcrypt._
import play.api.Logging
import scala.util.Success
import scala.util.Failure

object Hash extends Logging {

  private val log = Log.get

  def create(value: String): String = {

    log.debug("Encrypting a value")
    // Creating a salted hash
    val salt = generateSalt
    val hash = value.bcrypt(salt)
    // return hashed value
    hash

  }

  def validate(value: String, hash: String): Boolean = {

    // Validating the hash
    value.isBcryptedSafe(hash) match {
      case Success(result) => { // hash is valid - correct salt and number of rounds
        log.trace("Hash is safe")
        if (result) log.trace("Test hash matches stored hash") else log.trace("Test hash does not match stored hash")
        result // true if test hash matches the stored has, false if it does not
      }
      case Failure(failure) => {
        // Hash is invalid
        log.trace("Hash is not safe")
        false
      }
    }

  }

}

Use example:

// Password hashing
val hash = Hash.create(password)

// Password validation
def authenticate(email: String, password: String): Option[User] = {

    log.debug(s"Authenticating user: $email")

    // Get user
    read(email) match {
      case Some(user) => {
        // Compare password with the hashed value
        if (Hash.validate(password, user.hash)) Some(user) else None
      }
      case None => {
        // Cannot find user with this email
        log.trace(s"User not found")
        None
      }
    }
  }

Please see more at https://code.linedrop.io/guides/Ultimate-Guide-to-Building-a-Web-Application-with-Play-and-Scala/Utilities#PasswordHashing

Hash utility example: https://github.com/LineDrop/play-scala-web-application/blob/master/app/utilities/Hash.scala

User model example: https://github.com/LineDrop/play-scala-web-application/blob/master/app/models/User.scala

LineDrop
  • 181
  • 1
  • 5