I recently installed the Subway app on my phone and realised I didn't know my password. Having pressed the forgotten password link and submitting my email address I was horrified to find my password had been emailed to me! The implications here could be worse than you think.
First of all, it should never ever be possible to recover a user's password from a system like this. If you can do it, someone else can do it. That means if you use the password anywhere else (which you shouldn't) then any account where that password is used could be compromised. Second, email is not a secure form of communication. This is the reason banks will regularly tell you to never divulge sensitive information in an email, nor send you sensitive information in an email. Thirdly, it gives a strong indication that behind the scenes the other aspects of security may be equally as inadequate.
When you trust an organisation with your personal data you expect them to keep it under lock and key and to protect it. In this example, the protection of my password, Subway have failed spectacularly.
When creating an account for a user on your website or application there are some commonly accepted measures that should be taken to protect it. I'm working on the assumption here that the initial account creation and transmission of data took place over an SSL encrypted link, something I intend on verifying later with tcpdump. The focus of this blog will be primarily on the safe storage and verification of passwords and how to deal with forgotten passwords.
When logging in to a website you generally need to provide a username or email address and your password. These usernames and passwords are usually stored in a database. A website will check the credentials you supplied against the credentials stored in the database and if they match, will let you in. At this point you might think that storing your password in the database would be fine, only a select few people have access to this information. The problem with this is, if the database was ever exposed or someone gained access, with your email address and password how many other websites could they now go and log in to with your details? (remember my previous comment about using the same passwords) Also, if there are 1 million records in the database, the scope of the problem increases rapidly. You only need to read up on the PlayStation Network hackto see how bad it can get.
Securely Storing PasswordsWhen a password is stored in a database, you should never store the plain text version, that is, the version you type in the keyboard that is readable to humans. If someone gained access to this they could simply read your password and use it elsewhere to try and compromise other accounts. The password should be encrypted using a cryptographic hash function. Whilst this may sound fancy or complicated, all it basically means is it scrambles your password up until it looks like a string of random numbers and letters that a human can't read, this is known as the digest. To recover your password from the digest is considerably more difficult than simply reading the plain text password as a hash is 'one way'. This means it's very easy to generate a hash but the plain text input can't be directly derived from the digest. When a user logs in, instead of comparing the plain text password provided to the plain text password stored in the database, the website hashes the provided password and checks if it matches the digest stored in the database. If the digests match then the input to the hashes also matched and you can authenticate the user without ever storing their password. There are literally dozens of great websites out there with information on how to do this properly.
Add Some SeasoningIf you run a password through a specific crypto hash function the output will always be the same, no matter when you do it. This is how the website can hash the provided password each time and compare it to the stored digest to authenticate you. This still presents a problem if the database is ever breached and a malicious person gains access to your stored password digest. An attacker can use a lookup table which contains common passwords and their associated digests. By comparing these digests to your digest, if you have a common password they could match the digests and discover your password without ever having to really try. These tables can easily contain millions of entries and can compromise a weak password in a matter of seconds. One way of mitigating this type of attack and other similar methods is to add salt. In cryptography, salt refers to a random selection of numbers and letters that are added to your password before it is hashed. This can be stored in the database alongside your username and password and it can be stored in plain text. It should be unique to each user but generation of a random selection of numbers and letters is easy enough to accommodate. If your password, along with possibly other people in the database, was "MyPassword", a lookup table attack would quickly find a matching digest and reveal your password along with anyone else who shared it. If you're randomly assigned salt was "Ab34Hf78Kr" and this was appended to your password every time you logged in, the digest for "MyPassword" and "MyPasswordAb34Hf78Kr" would not match. It also wouldn't match any others users who had the password "MyPassword" as their salts would all have been different, resulting in a different digest. Whist adding salt will not prevent the attacker getting your password, it will slow them down tremendously. They now need to generate a new version of the lookup table for each unique salt with it appended to the end of the passwords. This in itself is no small task and is why it's important every user has a long and unique salt.
What Happens When I Forget?Whilst simply emailing a user their password is the easiest option to implement it is simply no longer acceptable from a security standpoint. The plain text password can be intercepted during transit and it quite simply should never have been stored in the first place. The most secure method to resolve a forgotten password is to send the user a reset link. These links contain a unique ID number and direct the user back to the website where the account is held which *should* be secured with an SSL certificate. This way we know the communications between the user and the website are secure and they can begin the password reset process.
From here the user can securely reset their password, which will be salted and hashed before being inserted in the database. Also, to be even more secure, the salt should be re-generated at this point so any previously constructed hash table containing the existing salt is now redundant.
Ultimately there is no real way to prevent someone obtaining and decrypting your password if they really want to and have the resources to do so. Storing hashed passwords and adding salt are 2 methods of slowing an attacker down out of many that are available and should be implemented as a minimum security level. The aim is to slow them down enough they deem it's not worthwhile, or, that the website that was breached has time to inform you so you can change your passwords elsewhere before other accounts are compromised too.
I realise this is a fairly high level description of the subject but if you have any questions or suggestions please let me know in the comments below.
Short URL: https://scotthel.me/PassReminder