So I completed my posts on the SHA hashing work, and a few people have said they really enjoyed it, one person said he liked the work on hashing and was going to use the code for password storage. He got upset when he saw my very angry face, and to be honest, I probably overreacted, but with good reason.
Simply put, you shouldn’t be using SHA and MD5 for password storage, the time has come where it is too simple to build tables of plain text to hashes, allowing for simple lookup attacks against collected hashes. The typical response to this is through the use of salting, but that only buys you some time, and typically takes quite some work to implement. Some people will use a single salt for all their passwords, and this reduces the effectiveness of salting.
The issue with SHA and MD5 is they don’t take into account how powerful computer systems have become, we need to make use of an algorithm that causes a significant amount of computational work. What is even better is an algorithm that we could increase this amount of work as the years went by, keeping those pesky tables at bay!
Well before we talk about the algorithms, let’s talk about workload factors! A work load factor is simply the amount of work that will be done for the generation of a hash of a plaintext. Increase the workload factor and the computer needs to do more.
Here is an example. We have an algorithm that when we use a workload factor for 10, it takes 10 seconds to generate the hash of a plain text, if we knew there were 1000000 possible plain texts, then to generate all the plaintext to hash maps would take 10 million seconds or 155 days. If we up that workload factor to 1000, then it will take 1000 million seconds or 31 years!
So what hashing algorithms allow us to set the workload? Well, here is a two major ones:
Both of these have a strong cryptographic heritage, both still use salting, they are well known and proven. Today, I am going to be using BCrypt, in particular the BCrypt.net, which is just a C# port of jBCrypt.
One final thing to point out, and this isn’t an algorithm design feature, more an implementation feature. A good implementation of a password hashing algorithm should allow us to have some users on one workload, and other users on another. A good implementation should allow us to have hashes of different workloads present in our system, without a huge effort to test a plain text against a hash. The BCrypt.net library outputs hashes of the following form:
I am not going to go into a full analysis of the string, but there is a few things to point out. The string will always start with $2a$, then the workload factor (here the default of 10), followed by another $, then the salt and the actual hash value. If I don’t specify a salt when generating a hash, the salt part of this string will change each time I call the hashing method!
Should we specify the salt? Should we specify the same salt for all users? The Answer is simply, no! If we do that, we can’t easily change it if needed, also, we are making things easier for the attackers. If we use the same salt, then if two users have the same password, they will have the same hash. Each user needs their own salt.
It should also be noted that the workload influences the salt, our workload defines the salt. If we want to be able to change the workload factor on the fly, we need independent salting. This is important to remember: Workload Factor makes salt which when combined with our input value becomes our hash value.
Let’s look at another example, here we are using unique salts, and a dynamic work load factor. To begin with, the workload factor is 5, Bob comes along, selecting a password of: “let me in”. The resulting hash is:
Now Alice comes along, she also uses the same password, her hash is:
The sysadmins then device to up the level of security, increasing the workload to 6. Even decides to use the same password, except this time, her hash is:
as we can see, her workload factor has increased. Note at this point, we have bob and Alice still on a work load factor of 5, only eve is using a factor of 6! If we are smart, we can have these users’ password hashes residing side by side.
Well that is it for Part 1, see you next time in Part 2.