As should hopefully be obvious at this point, the consideration put into the responsible management of passwords is huge. It is a constantly improving field and, as such, requires that technologists stay on top of recent recommendations and improvements. As researchers release new methods for hashing or stretching passwords, there is a constant evaluation of whether the newest methods will stand the test of time and whether the old ones will continue to hold strong. It's impossible to know what might be proved vulnerable to a clever trick tomorrow, and when that happens there is often scrambling to update all of the systems using the now insecure methodology.
Far too often, these updates aren't appropriately prioritized because using a new process behind the scenes isn't as exciting as new bells and whistles, or releasing the new version of your application on time. And asking all your users to update their passwords (which you don't, or at least shouldn't, have stored) doesn't go over super well either. So it is on everyone involved, programmer or not, to ensure that security is taken seriously and prioritized before a hacker steals old hashes and quickly learns everyone's password.
It's worth noting explicitly that many existing applications do not use modern methods for storing passwords. In some cases the passwords may be stored unaltered ("plaintext"). In others they may be hashed but not salted. Depending on the use-case and the data being protected this might be considered a low risk issue. However, given that many users reuse passwords across many websites, this could pose more of a threat to user's data than might be immediately apparent. For this reason, it is extremely important that users are careful about how and when they reuse passwords (ideally never). Because a breach at that home improvement forum you signed up for years ago could be the real reason your bank account gets compromised. Email accounts are especially important, since most password reset schemes send emails to ensure the real user is the one resetting the password. This means that someone that gets into your email account could theoretically get into any other website with that mechanism (this is why 2-factor authentication is important, ideally non-SMS 2FA).
All of that said, password storage methods are really a last-line-of-defense kind of issue. For someone to steal a database of password hashes (we don't actually keep them on a sheet of paper) they almost always need to get through several layers of network and server security first. But when those layers are compromised or bypassed, good password management decisions can really help lessen the blow.
In future blog posts we will most certainly cover other security-related topics (it's a common topic at Software Verde) and we hope you'll be back to check them out. However, if you're a programmer, or are otherwise interested in some more detail, please continue for our more technical recommendations on password management.
If you're a programmer you may be wondering what specific methods you should be using for your next application. The main impetus for this blog series was our own conversation about which specific methods were appropriate for an application we were writing. Giving concrete recommendations can be tough because they can often become out-dated by the time they are found again. We always recommend looking for recent recommendations and vulnerability reports whenever considering password handling algorithms or really any security-related topic. But we also know the comfort that comes from seeing what someone else did and the troubles or concerns they might have had along the way.
To start, the primary recommendations we found from recent sources were PBKDF2, bcrypt, scrypt, and Argon2. The two resources we found that we liked the most were this NCC Group blog post and this naked security article. Both were helpful in terms of determining the factors that make the algorithms secure and helping determine the appropriate parameters for those methods. We recommend reading both of those articles as they both cover the topic well and from different angles.
For the application we were building, we ultimately chose to go with PBKDF2 for a variety of reasons. Argon2 and scrypt both seemed like great contenders but they also seemed a bit too new to be comfortable with (this was several years ago, however, and both have seen much wider use since then). PBKDF2 also had the advantage of being included in Java 7 ("PBKDF2WithHmacSHA1") which has tons of usage examples available online. We were, at first, concerned about the fact that it used SHA-1 due to concerns about its security. However, we were comforted by the fact that HMAC is less affected by collisions in the hash function it uses than the hash function itself. This abated our concerns for the time-being but we are still working toward determining a better standard for future applications as others recommend.
Regardless of what option we choose in the future or what you choose for your next application, we absolutely recommend following the recommendations of the NCC Group post and determining an allowable timeframe for password checking and performing testing to find a set of parameters that fill that time on your servers. Also be sure to re-evaluate this whenever you upgrade your hardware to make sure you're running in an optimally secure fashion. Keep in mind that only passwords generated after that point will receive the increase (since changing the work factor will change the resulting hash/key) so having your users update their passwords after increasing your default work factor is a good idea. Alternatively, you could auto-update your stored value on login (since once you've verified the password with the old value you can reprocess it with the new work factor).
Furthermore, as you look into the current recommendations and evaluate which ones will work best for your project, track the rationale behind your decisions and document all of the options you considered. That way when you are re-evaluating in the future you can reference those old discussions and make the best decision you can without having to reverse-engineer your prior understanding of each option and your constraints. The amazing option that was too new in the past might be better established by the time you look back. The option that was too difficult to get working might be easier use after the software upgrade you did for other reasons. As our systems evolve, so should our view of the options available to us.
This section is called "Not So Final" precisely because the algorithms and processes discussed here will almost certainly be outdated one day. Regardless of when you're reading this, be sure to check for any future blog posts we may have on the topic and confirm what we say with other sources. If you disagree with something we've stated here, let us know. We are always working toward finding the best ways to solve problems and we'd love to hear if you have any recommendations we didn't mention or if you find that something we mentioned is no longer accurate.
Thank you to everyone who took the time to read this series. We hope you enjoyed reading it as much as we've enjoyed sharing it with you.