Per-User Password Hashing Algorithms

Multithreaded JavaScript has been published with O'Reilly!
DEPRECATED: This post may no longer be relevant or contain industry best-practices.

If this article is greek to you, check out my introduction to Encryption, Hashing, and Salting.

I recently heard that Envato's Tuts+ Premium was hacked on June 26th, and a database of username/passwords were taken. The worst part of all, this site wasn't encrypting the passwords at all! This is the absolute worst-case situation for having a database of username/passwords stolen. There has been a lot of user database compromises lately. Last.fm was hacked and became public on June 7th (MD5 no salt), LinkedIn was hacked on June 6th (SHA1 no salt), eHarmony was hacked as well on June 6th (MD5 no salt).

What's the big deal about having hashed passwords stolen? When passwords are encrypted with a common one-way hashing function, attackers can keep a database of the encrypted values as well as their plaintext counterparts. Such a database is called a rainbow table.

This just really shows that apps storing user credentials need to employ better encryption methods.

Here's kind of the existing levels of password storage and their usefulness:

Plaintext > Reversible Encryption > One-Way Hash > App-Wide Salting > Per-Account Salts

But, today I am proposing a new technique, which is per-account encryption algorithms. It sounds a little crazy, and I'm not sure if it is even a good idea (please provide some criticism). Here's basically how it works. Each user has a minimum of three pieces of data tied to them. Username, Password_Data, Password_Algorithm. When a user account is created, the Password_Algorithm is generated randomly. Here's an example of two different password algorithm's which two unique users may have:

SHA3(MD5($pass+"0NWOV_8")+SHA2("JE48LWV"+$pass)+SHA1(MD5($pass+"OWND87")+$pass)+$pass+"UEND73D"+$pass)
SHA2(SHA3($pass+"68W")+$pass+"UWOM969"+MD5(MD5($pass+"joweoi20")+"OWJO89"))

Of course, there would be a bunch of restrictions. Depth of functions would probably not want to exceed 4 or so. Anytime there is a hash function, the password should be contained within the input string somewhere. Total length of the algorithm string shouldn't exceed a certain number of characters.

Running these complex algorithms would cause authentication to slow down. Which is a good thing. It also means that attempts at running the algorithm many times against all possible passwords will be even slower.

The syntax for the algorithm might not be the easiest to parse, and I suspect there could be a better format for this, such as some sort of Reverse Polish Notation. Ideally, this algorithm syntax should be easily parseable and executable by multiple languages (PHP, JavaScript, Ruby, Python, you name it).

Hashing a hash reduces entropy, so in theory it increases the likely hood of collisions, but not knowing the algorithm ahead of time makes it pretty dang hard to find such collisions.

Let me know what you think of my crazy idea below. Keep in mind that bcrypt is probably better anyways.

Tags: #security
Thomas Hunter II Avatar

Thomas has contributed to dozens of enterprise Node.js services and has worked for a company dedicated to securing Node.js. He has spoken at several conferences on Node.js and JavaScript and is an O'Reilly published author.