Welcome to part 3 of our series on password management. Last time we left off on brute-force attacks and the threat they pose to using cryptographic hash functions as a method for password verification. As mentioned, rainbow tables make this even worse. So what do we do?
Realizing that rainbow tables and other brute-force attacks are a threat, we can employ the use of salts to work around it. A salt is a large random value that is added to a user's password before hashing it. In a sense, it makes the password much longer, ideally to the point where generating a rainbow table for values that large would be impractical (since generating all possible passwords with up to 15 characters is much easier than doing so for 40 characters). Also, since each user will have their own random salt, the thief would no longer be able to tell when passwords are being re-used, since the hash values would be different.
On the other hand, since the user isn't creating or remembering the salt, we need to store it with their hash so that when we validate their password we can add it on again. So someone stealing the password sheet would know what that salt was and be able to use it to their advantage when trying to determine the password. So it doesn't necessarily eliminate the problem of brute-force attacks, but it does make the thief have to do it after stealing the sheet, buying us back that post-theft recovery time. It also means that they will have to brute-force each password separately with its unique salt. This is why the salt needs to be random. If all we did was reuse the same salt (essentially putting a password of our own back-to-back with the user's) then if/when the thief got our reused value they would be able to create a rainbow table.
Salted hashing was, for a long time, the standard way to solve this problem. However, as computers continue to get exponentially faster the speed with which determined thieves can brute-force their way through possible passwords keeps getting faster and faster. Those with large budgets can employ many expensive computers to work together to get the job done much more quickly than we would like. So we have been forced to loosen our speed constraint and are shifting toward using processes that intentionally take longer to compute.
Ideally, we'd use a process that takes a lot of time to calculate once, but users aren't willing to wait 15 seconds just to find out that they had a typo in their password and need to try again and wait another 15 seconds. Even a few seconds could be frustrating, so there's a balancing act. We need a process that we can tune to take a long as possible without unduly impacting the user experience. Enter key-stretching algorithms (password stretching).
Generally speaking, at least in the context of storing passwords, the current recommendation (as of June 2017) is to use a process which repeatedly alters a provided password to generate a larger password from it. Salting is used here and hash functions may be as well. Without getting too much into the details, the idea is that each step of this process should be unique to the provided password. The number of steps can be increased over time to ensure that the values the passwords are checked against are increasingly difficult to create. Ideally this should be increased in conjunction with improvements to computer hardware so that the user experience remains the same but more work is being done (since more processing power means more work can be completed in the same amount of time).
This brings us to the modern approach, but we aren't done just yet. Next time we'll be going over what to take away from this series and how users and programmers alike can make sure that they're keeping the right things in mind when it comes to managing passwords.