1

I'm using dcodeIO/bcrypt.js to hash user password on login before sending it to the server. On the server-side I then hash this again and compare with the doubly hashed stored password.

The question: How do I generate a constant salt per user on the client side before hashing the password? My thought was to use the user email as salt, perhaps together with the site's domain-name as well. But BCrypt complains when using this as salt:

dcodeIO.bcrypt.hash(password, emailAddress + "@domain.com", function (err, hash) ...

Outputs:

Invalid salt version

A generated salt look like this:

$2a$10$d.EzcWyzdCsazYx/IfElQO

Can I go from my custom salt (emailaddress etc) to a format that BCrypt accepts? Is this the wrong way of doing it? Since the user has not logged in yet I can't get the salt used when creating the account.

Andreas Zita
  • 113
  • 1
  • 4

2 Answers2

2

A bcrypt salt must be 128-bit long, that's why using email addresses can't work, since their length aren't fixed.

Moreover, salts used for password hashing must be unique. An email address, even if marked as unique in your database, could also be used as a salt in another database on another website. That's why you should generate one randomly.

Since salts aren't secret, you could fetch the salt associated with the email address the user wants to login with (via AJAX), perform the first bcrypt calculation, and send the result to the server.

But that's not a good idea from a security perspective. Please see this question to understand why hashing the password client-side isn't needed.

Benoit Esnard
  • 13,979
  • 7
  • 65
  • 65
  • I'm using my domain-name to make the salt unique per site and yes I'll be using SSL so I don't really need to hash the password client side I guess. But in any case, can I generate a 128-bit salt with bcrypt (from email+domain?) – Andreas Zita May 07 '17 at 14:53
  • @AndreasZita - And what if the email changes, you can't login anymore? You would have to store the email-salt somewhere, and with the same effort you can store a really random salt. Client side hashing can be done, but its purpose is not to make the hash more safe, it is done to relieve the server from the heavy work. – martinstoeckli May 07 '17 at 16:38
1

Salts need to be globally unique

Although this may not apply to your case, there are good reasons why salts cannot just be derived from a username or other constant data about the user.

Consider a user Alice who using the same password on multiple sites, both of which use her username as the salt. This will lead to the same hash appearing in both. Using a combination of the username and the site itself for the salt should address that specific concern.

Salt format

As others have pointed out, your salt is in the wrong format. You could perform a hash of the email address and site name and then truncate that hash to 16 bytes.

Server side hashing is still required

There are some things that client side hashing offers, but it can never (well, hardly ever) be a replacement for server side hashing. And unless coupled with some other useful constructions, client side hashing really doesn't offer much at all. Mostly it helps save the server some work.

The most obvious apparent advantage of client side hashing is that it makes it harder for the service to learn the user's password as the password itself is not being sent. But that advantage only works under a very limited threat model. You need an attacker who can capture the password server side when it is sent from the client, but cannot modify the JavaScript in the login page.

There is also a sort of pass the hash mentality here. The hash that the client sends is a secret. If it is captured (say in transit) it can be used by an attacker to log in to the service just as knowledge of the source password would. So just because the hash doesn't look like a secret (while the source password does), it is an authentication proof.

I'm not saying that client side hashing is of no use, but it is of much less use that people expect under most circumstances.

Jeffrey Goldberg
  • 6,420
  • 17
  • 21
  • Client-side hashing seem more like a courtesy to the user to not accidentally reveal the "true" password (In combination with using transport security). And server-side hashing a requirement to mitigate database-theft. But I don't get why client side hashing would save work for the server? I mean wouldn't it still have to apply the same slow hash no matter how the original password was hashed client side? – Andreas Zita May 11 '17 at 12:18
  • 2
    @AndreasZita - This is the main reason for client side hashing, to releave the server from the heavy work. The client will take on the heavy part with key-stretching (e.g. BCrypt), and the server can just calculate a fast hash (like SHA-256). This is safe because the BCrypt hash is such a strong password, that it cannot be brute-forced successfully, even with a fast hash, and because the time consuming part must still be done in a dictionary attack. – martinstoeckli May 26 '17 at 06:23