This is my spin on User passwords should never leave the browser and the JavaScript and Web Cryptography: PBKDF2

TL;DR

The user enters their password, but you send a PBKDF2 generated key based on a pre-chosen shared salt, their password and (perhaps) their username (to make the salt unique per user).

The code is at the bottom, just scroll down.

Live Demo

Instead of sending the user's password to your server, send a proof of that password.

Preselected Values

WARNING: It's important that your application salt be the same for iOS, Android, and the Web.

The goal of this is to send proof that the user provided their secret. If you change the salt or algorithm used between platforms, then the Secure Remote Password would only work on that one device.

What the User Enters

Computed Salt

Note: The difference between using the app salt only or the app salt plus a user-specific salt (such as the username) is the difference between an attacker needing to create a single rainbow table, or one for each user. However, since you'll still be using PBKDF2, bcrypt, or scrypt on your server, it doesn't matter that much.

The Secure Remote Password

Explanation

jamais deux sans trois

One thing we know about the universe is that if something has happened once, it is infinitely more likely to happen again.

Heartbleed showed us that no one measure is sufficient to protect us, or our users.

So what's the best way to keep a secret?

I used to work at a company where occassionally the boss would come into the software room and exclaim things like "Great news guys! We just got a contract with ACME corp! But remember, this is strictly proprietary so don't tell anyone, not even your wives."

There are two well-accepted philosophies, but that isn't one of them.

Philosophy 1:

The best way to keep a secret is to never know it in the first place.

Philosophy 2:

Dead men tell no tales.

However, it turns out that the second isn't actually true.

So... Philosophy 1 it is.

Never take a user's password!!!

How do you verify a secret you don't know?

It seems like this should be rather obvious, because it's already standard practice on the server.

DERIVED_KEY = HASH(SALT + PASSPHRASE)

The DERIVED_KEY is the hashsum (say SHA-256, for example) of the SALT (a long, unique value tied to the user) and the PASSPHRASE.

One immediate problem that you might see with this approach is that you're probably used to generating the SALT when you create a user and retrieving it during every login attempt.

The solution is to have a public SALT for your application (or for each tenant of your application) and join that to your user identifier.

SALT_2 = HASH(SALT + USERNAME)
DERIVED_KEY = HASH(SALT_2 + PASSPHRASE)

Remember, the purpose of the SALT is to make it difficult to precompute a rainbow table.

It doesn't need to be secret, it just needs to be different for each user.

Allowing either USERNAME or EMAIL

In the case that you allow a user to login with either a USERNAME or an EMAIL, you have a couple of options:

Treat logins separate from accounts

Allow the user to have multiple logins that have access to the same account. When they update the passphrase for one login, ask them if they would like to also update the passphrase for the other logins linked to the account as well.

Store both DERIVED_KEYs

Only use an email

Simplify things and require a user to have an email address as their login, but be sure to allow them to pick a displayname / username for their account.

Code

The Unicode Problem

See my UtahJS Web Crypto Presentation for details, but suffice it to say

"I'm a ☢ ☃ who speaks 中国语文, loves 𝄢 guitar, and plays in 💩!"

That string has 58 characters, but JavaScript reports 60. It comes out to 80 bytes, or perhaps just 76, depending on how you encode it.

Since there is no standard way to convert a Unicode String to a UTF-8 TypedArray Buffer, I'm using Unibabel.js to perform the conversion in the code below.

The derive-key function, wrapped in a bow, just for you:

 

The demo:

 

By AJ ONeal

If you loved this and want more like it, sign up!


Did I make your day?
Buy me a coffeeBuy me a coffee  

(you can learn about the bigger picture I'm working towards on my patreon page )