Crypto.passwordHash() is not secure for hashing passwords.

5,035 views
Skip to first unread message

Ron Gross

unread,
Feb 1, 2012, 7:46:43 AM2/1/12
to play-fr...@googlegroups.com
Read this before proceeding.

tl;dr - all the standard hashes (MD5, SHA1, SHA256, SHA512, SHA-3, ...) are insecure for storing passwords (if you just do one hash pass), because they're just too fast.

The proposed solution is to use a function that is deliberately slow, like bcrypt (in industry use for 12 years).
Another alternative is to apply a primitive like SHA1 iteratively, feeding the output back to itself.
I'm not sure which approach is better. This post suggests bcrypt.

Play's Crypto.passwordHash, by default, does one pass of MD5.

I'll be switching to bcrypt, I suggest Play defaults change to something more secure as well.

Ron Gross

unread,
Feb 1, 2012, 7:53:37 AM2/1/12
to play-fr...@googlegroups.com
Link to a jbcrypt, a java implementation of bcrypt.

Adam Brimo

unread,
Feb 1, 2012, 7:57:39 AM2/1/12
to play-fr...@googlegroups.com
Thanks for bringing this up. I completely agree, using a different password encryption library was one of the first things we did when we started using Play. 

There is another good explanation of encrypting password on the Jasypt site: http://www.jasypt.org/howtoencryptuserpasswords.html

GrailsDeveloper

unread,
Feb 1, 2012, 2:21:55 PM2/1/12
to play-framework
You can use http://bazaar.launchpad.net/~opensource21/+junk/permsec/view/head:/psec/app/de/ppi/util/PasswordTools.java
too. It uses salts and you can define the algorithm and rounds
depending on how safe you want it. You can change the default method
because the method is stored in the hash.
How ever it would be great to see in play 2.0 a safe implementation!

Niels

David Galichet

unread,
Feb 2, 2012, 2:36:50 AM2/2/12
to play-framework
You can also salt your password with the username for example :
Codecs.sha1(username + password). This will prevent quite efficiently
against reverse dictionary or rainbow tables attacks.

David.

On 1 fév, 20:21, GrailsDeveloper <opensourc...@googlemail.com> wrote:
> You can usehttp://bazaar.launchpad.net/~opensource21/+junk/permsec/view/head:/ps...

Ron Gross

unread,
Feb 2, 2012, 3:02:09 AM2/2/12
to play-fr...@googlegroups.com

Yes, but won't prevent a brute force attack. A single standard hash is not secure even with salting.

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To post to this group, send email to play-fr...@googlegroups.com.
To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.

Will Sargent

unread,
Feb 2, 2012, 3:11:16 AM2/2/12
to play-fr...@googlegroups.com
What Ron said, in spades.  Even HMAC-SHA512 has the problem that digest algorithms are fast and storing the digest is easy.  PBKDF2 or bcrypt is really the way to go for storing passwords.  

It's worthwhile to checkout this white paper as well: web_20_crypto.pdf

Will.

Havoc Pennington

unread,
Feb 2, 2012, 11:38:41 AM2/2/12
to play-fr...@googlegroups.com
Also https://wiki.mozilla.org/WebAppSec/Secure_Coding_Guidelines has good stuff.

Here is some code that:

- hashes each password with a secret from application.conf, you can
change this secret to invalidate all passwords, also to crack
passwords you need both this secret (from disk) and the salt (from
database)
- the SHA512 hash is then bcrypt'd with a salt

It uses http://www.mindrot.org/projects/jBCrypt/ (afaik you just have
to cut-and-paste the BCrypt.java file, it doesn't seem to be
Maven-published)

Havoc


/**
* Copyright (C) 2011 Havoc Pennington
* Licensed under the Apache License, Version 2.0
*/

package security

import java.util.UUID
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import org.apache.commons.codec.binary.Hex
import org.apache.commons.codec.digest.DigestUtils

import bcrypt.BCrypt;

object Algorithms {

// HMAC SHA-512 hash
private def signature(message: String, key: String): String = {
require(key.length > 0)

val mac = Mac.getInstance("HmacSHA512");
val signingKey = new SecretKeySpec(key.getBytes("utf-8"), "HmacSHA512");
mac.init(signingKey);
val messageBytes = message.getBytes("utf-8");
val resultBytes = mac.doFinal(messageBytes);

new String(Hex.encodeHex(resultBytes))
}

def hashPassword(password: String, serverWidePasswordSecret: String) = {
if (password.length == 0)
throw new IllegalArgumentException("Password may not be zero-length")

// we add the server's secret key to the password,
// the idea is to require stealing both the server
// key and the database, which might raise the bar
// a bit.
val intermediate = signature(password, serverWidePasswordSecret)

BCrypt.hashpw(intermediate, BCrypt.gensalt())
}

def checkPassword(password: String, passwordHash: String,
serverWidePasswordSecret: String) = {
val intermediate = signature(password, serverWidePasswordSecret)
BCrypt.checkpw(intermediate, passwordHash)
}
}

Ron Gross

unread,
Feb 2, 2012, 12:43:01 PM2/2/12
to play-fr...@googlegroups.com
Good idea about using both SHA512 and Bcrypt!

jbcrypt is available from maven. Here is the deps.yml line:

    - org.mindrot -> jbcrypt 0.3m

My code seems simpler than what you wrote:

public String hashPassword(User user, String password) {
        switch (user.hashingAlgorithm) {
            case BCRYPT_SHA512: // our own enum
                return Crypto.passwordHash(org.mindrot.jbcrypt.BCrypt.hashpw(password, user.salt), Crypto.HashType.SHA512);

            default:
                throw new RuntimeException("Unsupported hash algorithm: " + user.hashingAlgorithm);
        }
    }

The user salt is initialized with:

user.salt = org.mindrot.jbcrypt.BCrypt.gensalt();




 }
}

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To post to this group, send email to play-fr...@googlegroups.com.
To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.

Eishay Smith

unread,
Feb 2, 2012, 4:54:33 PM2/2/12
to play-fr...@googlegroups.com
Bcrypt is definitely the way to go for passwords making brute force much harder. Is the inherit value in adding more encryption algorithms to the mix or is it specifically SHA512? 

Ron Gross

unread,
Feb 2, 2012, 5:11:23 PM2/2/12
to play-fr...@googlegroups.com
I think there are two advantages to using both algorithms together:

1. It's easier to add the app-specific crypto seed. So, in order to crack it you need to get both the salts from the DB and the crypto seed from disk, just like Havoc said.

2. It feels a bit safer because even if there's a slight hidden bug in the bcrypt implementation (because this library is less wildly used), it's possible (although not guaranteed) that an extra SHA512 round (trusted, often used library) will make exploiting the bug harder.

Havoc Pennington

unread,
Feb 2, 2012, 6:17:06 PM2/2/12
to play-fr...@googlegroups.com
Yep. The rationale on the Mozilla guidelines page is:

* "The nonce for the hmac value is designed to be stored on the file
system and not in the databases storing the password hashes. In the
event of a compromise of hash values due to SQL injection, the nonce
will still be an unknown value since it would not be compromised from
the file system. This significantly increases the complexity of brute
forcing the compromised hashes considering both bcrypt and a large
unknown nonce value"
* "The hmac operation is simply used as a secondary defense in the
event there is a design weakness with bcrypt that could leak
information about the password or aid an attacker"

I guess they use 512 because why not use the maximally-strong SHA
flavor (this is the rare case where having an algorithm be slow is
desirable, so you can use the slowest-but-strongest SHA).

Imagine you're using a database-as-a-service provider or something
like that, and the db provider is hacked, but your app server is not -
the extra secret hashed in from application.conf could save you in
that case and make it impossible to search for "123password" and
"letmein" and all those other common crappy passwords. Without the
extra secret, people can still search for them... even though bcrypt
is slow and the salt requires recomputing the hash for every
user-password combo, some of the common passwords are common enough
they'd likely get one eventually.

Havoc

R. Rajesh Jeba Anbiah

unread,
Feb 3, 2012, 12:28:31 AM2/3/12
to play-framework

On Feb 2, 10:43 pm, Ron Gross <ron.gr...@gmail.com> wrote:
<snip>
> The user salt is initialized with:
>
> user.salt = org.mindrot.jbcrypt.BCrypt.gensalt();

Different salt for different users? How to store the salts safely?

Drew Kutcharian

unread,
Feb 3, 2012, 12:42:39 AM2/3/12
to play-fr...@googlegroups.com
Just to chime in, I did a bit of a research on what's the best way to store passwords a couple of years ago. The thing with hash functions is that they are mostly built to just do that, create hashes quickly. So by definition, if you are using a hashing function that is fast, it's not going to be secure. So I moved to BCrypt a couple of years back.

The thing to note is that, even when using BCrypt, you need generate a brand new salt for each password. The salt can be anything random and the salt doesn't need to be protected. So you can store your password in a "salt : hash" format where the first part is the plaintext salt. The salt has no use to attackers, it's only used to ensure that the hashes are not predictable and the attacker cannot infer relationships between the hashes. It's pretty much the same idea as IVs (Initialization Vectors) [1] in encryption.

HTH,

Drew

[1] http://en.wikipedia.org/wiki/Initialization_vector

R. Rajesh Jeba Anbiah

unread,
Feb 3, 2012, 12:58:49 AM2/3/12
to play-framework
On Feb 3, 10:42 am, Drew Kutcharian <d...@venarc.com> wrote:
<snip>
> The salt has no use to attackers, it's only used to ensure that the hashes are not predictable
> and the attacker cannot infer relationships between the hashes.

Thanks for the explanation.

Jean-Francois Im

unread,
Feb 3, 2012, 11:01:48 AM2/3/12
to play-fr...@googlegroups.com
On Fri, Feb 3, 2012 at 00:42, Drew Kutcharian <dr...@venarc.com> wrote:
> The thing to note is that, even when using BCrypt, you need generate a brand new salt for each password. The salt can be anything random and the salt doesn't need to be protected. So you can store your password in a "salt : hash" format where the first part is the plaintext salt. The salt has no use to attackers, it's only used to ensure that the hashes are not predictable and the attacker cannot infer relationships between the hashes. It's pretty much the same idea as IVs (Initialization Vectors) [1] in encryption.

Are you sure that you need to actually store a salt value? I was under
the impression that BCrypt stored it implicitly and there is no
documentation of such a requirement in the jbcrypt documentation. From
a quick test:

$ scala -classpath lib/jbcrypt-0.3m.jar
Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM,
Java 1.6.0_29).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import org.mindrot.jbcrypt._
import org.mindrot.jbcrypt._

scala> BCrypt.hashpw("hello", BCrypt.gensalt())
res0: java.lang.String =
$2a$10$76eXtd9ab8sbZWLlGxCmfOVr2ENud6JFOTyp3L9sJokSZwROChIgy

scala> BCrypt.hashpw("hello", BCrypt.gensalt())
res1: java.lang.String =
$2a$10$F5g.8C3b.3sBBm.LCbCbw.N/2QvHNTwJDCX4aCgRNz0UL.lqv/CpO

scala> BCrypt.checkpw("hello", res0)
res2: Boolean = true

scala> BCrypt.checkpw("hello", res1)
res3: Boolean = true

scala> BCrypt.checkpw("hello!", res1)
res4: Boolean = false

Drew Kutcharian

unread,
Feb 3, 2012, 1:01:30 PM2/3/12
to play-fr...@googlegroups.com
Yes, you're right about jBcrypt. It actually includes the salt and length of the salt in the encoded string that it returns using $ as delimiters. I was just trying to explain how salts are usually stored and they don't need to be encrypted.

-- Drew

Rakesh Waghela

unread,
Feb 3, 2012, 3:28:12 PM2/3/12
to play-fr...@googlegroups.com

Alex Hanschke

unread,
Jun 15, 2012, 2:40:00 PM6/15/12
to play-fr...@googlegroups.com
Found a good discussion on password security here: http://throwingfire.com/storing-passwords-securely/

Lars Støttrup Nielsen

unread,
Apr 14, 2013, 3:00:52 PM4/14/13
to play-fr...@googlegroups.com
Probably because it wasn't secure enough by todays standards.
Imagine a GPU cracker such as hashcat on a botnet of 10000 computers.
You suddenly have a frightening amount of computational power available. The SHA algorithms are fast, which is the main problem.

On Tuesday, 29 January 2013 22:30:39 UTC+1, sts2055 wrote:
New play webdev here.

I am trying to implement an encryption method for user data that is to be stored in a database (mysql). I'm following the examples listed in this discussion, however, the Crypto class in play 2.0.4 does not include the passwordHash method any longer. I found the old Crypto java source (https://github.com/playframework/play/blob/master/framework/src/play/libs/Crypto.java), however, since the method has been removed I am now wondering if something new has come up with regards to the Play Framework and should be used instead.

Does anybody know why the method has been removed?


On Wednesday, July 25, 2012 10:02:18 AM UTC+2, Gijsbert Peijs wrote:
Hi Ron,

The dependency works like a charm and also your code example is really helpful but there is one thing that breaks the example: The order of hashing. 

jBCrypt stores it's randomly generated salts in the password hash which is a really great idea because it allows BCrypt.checkpw to work without you providing the salt. But if you hash the BCrypt password with SHA512, there is no way to determine the salt again so BCrypt.checkpw won't work. By first applying the SHA512 hash, you can encrypt the hashed password with BCrypt with a random salt. Now all you have to do is SHA512-hash the plain text password the user provides and use BCrypt.checkpw to validate against the password stored in the database.
 
 

In code this looks something like:

    /*
     * hash passwords. First SHA512 using Play's internal Crypto and subsequently BCrypt (with random Salt).
     */
    public static String hashPassword(String password) {
        String salt = BCrypt.gensalt();
        return BCrypt.hashpw(Crypto.passwordHash(password, Crypto.HashType.SHA512), salt);
    }
    
    /*
     * encrypts the provided plain text password to SHA512 and then uses BCrypt to validate against the stored double encrypted password.
     */
    public static boolean matchPasswords(String providedPassword, String storedPassword) {
        String sha512Password = Crypto.passwordHash(providedPassword, Crypto.HashType.SHA512); // Only SHA512, no BCrypt
        return BCrypt.checkpw(sha512Password, storedPassword);
    }

Hope this is of any use.

Regards,

Gijs.




Op donderdag 2 februari 2012 18:43:01 UTC+1 schreef Ron Gross het volgende:

Torben

unread,
Apr 16, 2013, 2:17:57 PM4/16/13
to play-fr...@googlegroups.com
I agree and we started using BCrypt/jBCrypt on passwords for many of the reasons discussed here. I highly recommend it :)

Ryan Tanner

unread,
Apr 17, 2013, 1:03:56 AM4/17/13
to play-fr...@googlegroups.com
You could also consider the play2-auth library, it uses BCrypt in its default implementation.  

Reply all
Reply to author
Forward
0 new messages