In modern web applications, the users of the site create accounts to gain access to customized data. When creating a signup/login process that requires a username and password, the credentials need to be stored and secured in a way that blocks unauthorized users. Furthermore, the passwords themselves need to be protected in case a hacker gets access to your data. In this post, we will review 3 schemes most commonly used today for storing and retrieving sensitive information.
Plain text scheme
The most basic (and unfortunately common) way to store user passwords is plain text. In this scheme, the login ID and the passwords are stored as plain text pairs in the database. When the user inputs his credentials, they are compared to the stored values and an appropriate response is returned. This is most commonly seen in legacy sites that have not yet upgraded their password schemes.
When a site sends you your actual password on the signup process or on retrieve a forgotten password, they are using this totally unsecure method of saving your password. Anybody who has access to the database (or the underlying storage) can see your password.
One way Hashed passwords scheme
The next level of password encryption is seen when developers first start learning about cryptography. They instantly gravitate towards algorithms that generate a one way hash of the password and store that hash in the database. Most common (but wrong) algorithms that get used are MD5 and SHA-1. These algorithms have been proven to be unsafe and should not be used. In the .NET world, the SDL (Security Development Lifecycle) has banned the use of these 2 algorithms. The recommended algorithm at this writing is SHA512.
The disadvantage of such a scheme is that a dictionary or brute force attack and compromise your entire user base. But using this scheme is better than storing passwords in plain text. Also, it protects your user’s confidential information from prying employees who have access to the production database.
Let’s look at C# code to see how we can build this algorithm. The first function we will look at this the encryption algorithm itself. Then we will review how to use this function to Generate the password and then how to verify it.
As you can see above, the framework provides a very easy way to generated hashed bytes of an input string. Now let’s look at the wrapper functions to generate the password and verify it.
The wrapper functions themselves are simple and should be self explanatory.
Random and variable length salted hashed password scheme
Salt, a random set of bytes, is used to make unauthorized decrypting of a message more difficult. A dictionary attack is an attack in which the attacker attempts to decrypt an encrypted message by comparing the encrypted value with previously computed encrypted values for the most likely keys. This attack is made much more difficult by the introduction of salt, or random bytes, at the beginning or end of the password before the key derivation.
This password algorithm scheme employs the following steps to secure the use data:
1. Generates a truly random number between 8 and 24. This will be used to determine the length of the random salt.
2. Using this variable length, it then generates a random salt.
3. Next the user input is encrypted using this variable length random salt bytes using SHA512 crypto algorithm. In addition, the algorithm performs 1000 (or more) passes over the hashed output to provide a higher level of security.
As you can see above, the Encrypt functions does a few steps. It first converts the user input string to bytes. It then inserts the un-encrypted salt bytes at the beginning of the user bytes. Next, using the SHA512 crypto algorithm, it runs a 100 passes over the salt+input byte array to generate a strong hash. Next, it appends this variable length random un-encrypted salt to the hashed encrypted salt+input byte array and also sets the first byte to hold the length of the salt. Lastly, it converts this to a Base64 encoded string and returns it to the user.
Now that we have the core algorithm under our belt, let’s look at the final routines on how to generate the password and verify it.
4. Generate the password and verify functions.
The GetPassword() function is self-explanatory. You first generate a random number, get the variable length random salt bytes, use this to generate the password bytes, convert to Base64 encoded string and return this value.
The VerifyPassword() has a slightly different logic. Since each encrypted password uses a variable length random salt bytes, it first needs to get that information out of the encrypted password. Then using these salt bytes, it needs to re-run the encryption algorithm to regenerate the password. Remember, this is still using the one-way hashing concepts as that is the strongest way to ensure password safety.
In a production environment, you can do some additional variations where the un-encrypted variable length random salt bytes are inserted at the end, or a random location or in a separate encrypted string in a different column.
I hope this article will be helpful in helping you secure your customer’s private data from the prying eyes of the hackers. I would love to hear your feedback.