There are many aspects of web security, but one that seems to need particular attention is passwords. Passwords are perhaps the single the most sensitive piece of information that users should keep private online, even more than personal information such as birthdate, social security number, credit card number, bank account number, mother’s maiden name, or favorite childhood pet, since almost all of those could potentially be researched or else known by at least friends or family members, not to mention that, if any of those are stored by a website, whoever has the password can go in and access them anyway. And since users should go to extreme lengths to keep passwords private, so should websites.
What’s so special about passwords?
Why are passwords so important? Because not only are they the keys to the kingdom of the site they are meant for, giving unfettered access to everything in that user’s account (including all of that other sensitive information), but us being humans struggling to remember the passwords for probably dozens of different sites or services, we tend to re-use the same passwords in more than one place, and usually in combination with the same email or username, too. This means that compromising a user’s password on one site means potentially compromising all of their online activity and accounts, which may give access to virtually all of their personal information and communications.
While it’s easy to say “use a complicated long passphrase that is unique for each site”, in practice we human beings struggle with such a challenge. And while it’s equally easy to say “use a password manager”, the reality is we often have to log in from different devices, and most people don’t use password managers even on their primary device. So that’s where web developers come in, is effectively making passwords unique while maintaining other best practices in their handling.
A lot of these recommendations are basic, but I still run into sites that obviously store passwords as plaintext – they’ll even helpfully email your password to you if you forget it! Haven’t we all heard enough times about site break-ins that exposed the entire password database? It’s happened to countless sites large and small. In combination with the extreme speed of password hash cracking these days, that’s bad news. And of course the many sites using regular unencrypted http connections during login, exposing your password to anyone who happens to be sniffing the network connection somewhere along the line, which is easily done on many WiFi connections.
So here are some rules for handling passwords:
- Never transmit a password over an unencrypted connection. If your site allows user sign-ups, even for “casual” accounts with little sensitive personal information, you MUST get a signed SSL certificate (maybe $50 per year) for your site, and not only enable SSL, but also at least strongly encourage, or preferably require, the use of https in order to log in.
- Of course you shouldn’t just hash the password alone, it should be hashed with the unique username (to distinguish it from anyone else using the same password on that site) and a string unique to the site itself (to distinguish it from the same username/password combination on a different site).
- Yes, you still need to hash again on the server (see next), and yes the connection should be encrypted already (see previous), but this is a “defense in depth” measure.
- Use the best (hardest to crack) hashing function available, but CPU and memory limits, particularly on mobile devices, might preclude using the same method as is used for the normal server-side hashing mentioned next. (Note: since few if any websites use client-side hashing to help protect the uniqueness of user passwords across sites, users themselves can use a tool like this secure password generator, explained here. This code might be a good starting point for client-side password hashing as well.)
- Hash received passwords on the server using scrypt before storage or comparison to a stored password verifier. For this, do not use MD5, do not use SHA-1, don’t even use SHA-256 or SHA-512 (though I guess you could use one of the latter with enough rounds of hashing, if you must). Use scrypt, or at worst bcrypt or pbkdf2 (though either of those can be cracked thousands of times faster than scrypt). Bcrypt is designed to run inefficiently even when run across a large number of CPUs; scrypt has the added property of being memory inefficient to parallelize as well as being CPU inefficient.
- Keep the password file as secure as possible. Make sure it’s readable and writable only by the proper users, audit access to it, and preferably even keep it on a separate server from the main web server, with physical security, limited functionality, different/limited user access, no direct access to the Internet, and only allowing connections to it from the web server and only for purposes of querying the “is this password good for this username?” service.
- Introduce a delay between login attempts if they make too many wrong guesses. Make this delay increase gradually the more wrong guesses they make. For instance, after five wrong guesses, add a five-second delay, then increase it to 10 seconds, then 20, etc. Don’t ever actually lock them out of their account, just make them wait longer periods of time before being able to guess again. (Of course after that time expires without another attempt, roll back the delay until eventually they would have no delay again.)
- Encourage strong passwords and forbid especially weak ones. A “strength meter” (color coded and/or numerical/length) can encourage stronger passwords (longer and with more entropy), but there should also be a minimum requirement to accept a password. I’d suggest the following as a minimum:
- Require at least eight characters, but strongly encourage more (and allow a large number, perhaps as many as 256 characters).
- Require the password contain at least two types of characters (from lowercase letters, uppercase letters, numbers, or symbols), but strongly encourage at least three and preferably all four types of characters be present, as well as more than one of each.
- Institute a solid password recovery process. I think I’ll save this for a separate post, as it can be pretty involved.
Handling security isn’t necessarily easy, but not trying to do so is a grave disservice to a site’s users, even if the site involves little or no personal information, no commerce of any kind, etc. And properly handling passwords is one of the keystones of good security for sites with user logins.