Recently, I created an account with a site (which shall remain nameless). Everything seemed cool at first. Then I submitted my application and was instantly greeted with a message: “Thanks for signing up. Your username is XXXX and your password is XXXX”. Yes, the site showed my password in cleartext right on the screen.
That is pretty bad, right? Well, then something worse happened: I received an email. Can you guess what was in the email? That’s right: “Thanks for signing up. Your username is XXXX and your password is XXXX”. The password was sent to me in an email, again in cleartext.
I agree, Captain Picard.
If this story doesn’t make you facepalm, it should. Now, let’s talk about why careless password security is really bad, what policies and procedures you should have when storing passwords, and tips for a serious programmer to protect his passwords properly.
How Not to Protect Your Passwords
Storing passwords in cleartext. The number one practice that is completely wrong and indefensible is storing passwords in cleartext, as illustrated in the above example. This means whatever the user enters as their password will be directly stored in the database without any scrambling to prevent someone from seeing what they are. If you enter “p@ssw0rd!” as your password, then “p@ssw0rd!” is what is stored in the database. If someone gets into your database, they will be able to steal anyone’s credentials and impersonate them. Here’s a hint: if a website can send you your password, that means it is storing them in cleartext.
Allow any humans to see passwords. Here is a quiz question: who should be allowed to see users’ passwords? The answer: NO ONE. Don’t fool yourself into thinking that system administrators or help desk personnel need to see passwords in order to do their jobs. This is a very bad practice that leaves you open to social engineering attacks.
Encrypting passwords instead of hashing them. While there are times that an application may need to encrypt passwords, this should never be done with a user account’s password. The main reason for this is that if something is encrypted, it can be decrypted. You want to use hashes, or one-way functions that make it infeasible to get the input back out given the output. That brings us to our next point.
Using weak hashing algorithms or techniques. Just because you hash a password doesn’t mean it is safe. Which hashing algorithm did you use? Did you hash it only once and expect it to be okay? There are some subtleties to password hashing that can cause weaknesses and lead to compromise.
Do not despair. Let’s take a look at how developers can do it right.
How to Properly Protect Your Passwords
The first step to figuring out how to properly protect your passwords is understanding what kind of attacks the bad guys will use to try to get or guess those passwords. In this vein, let’s take a quick look at some of the top ways passwords are cracked today (a more complete list can be found here).
- Dictionary attack – This attack is, as it sounds, when a dictionary of words is used to try to guess a user’s password.
- Brute force attack – This is just a computer guessing every possible combination of alphanumeric characters until it finds the right password.
- Rainbow table attack – A rainbow table is a table of precomputed hashes of passwords. If password hashes are stolen from a database (for example, from an SQL injection attack), the attacker can simply search for the stolen hashes in the table to figure out the correct password. These nasty things are why just hashing your password is not enough.
- Offline cracking attack – Another side-effect of stolen hashes is that they are often published and made available. Once they are out, someone can just sit and use the above techniques until he/she finds some good passwords to steal.
- Collision-based attack – This is rarer and more difficult, but it is possible and thus you should be aware of it. A collision-based attack involves trying to find a set of characters that, when hashed, will produce the same output as your password. That way, the attacker does not have to have your actual password since, if the hashes match, the system will let you in. This is a big reason why you need to choose a good algorithm to ensure that the chances of collision are sufficiently low.
These attacks can be mitigated by two things: long, random passwords and secure cryptographic storage. It’s up to your users to provide good passwords (and you can help with policies), but it’s up to you to provide the secure storage.
The best way to provide that secure storage is by handling hashing correctly.
The first step in effective hashing is choosing the best hashing algorithm. This is the foundation of your password storage architecture and is arguably the most important decision. Some hash algorithms have been shown to be weak. MD5 is one such algorithm. It has been shown to produce collisions—a big red flag when it comes to security. Another argument against it is the fact that it is a fast and memory-efficient hashing algorithm. To critique this may seem strange at first but consider this: the more efficient the algorithm, the faster you can make guesses. This means that MD5 is much easier to crack using brute force than other hashing algorithms.
The secure hash (SHA) family of hashes was developed by the NSA and published as the FIPS 180 series of standards published by NIST. This family consists of SHA-1, SHA-2, and SHA-3. SHA-1 has recently been shown to have collision problems and has been deprecated by NIST since 2011. SHA-3 is still very new and isn’t in wide use yet. SHA-2 is your best bet for an algorithm that has withstood scrutiny and has shown itself to be unpredictable and collision-free. It comes in two flavors: SHA-256 and SHA-512, named after the amount of bits output by the algorithms.
We spoke earlier of rainbow table attacks. These are the reason that just hashing your passwords before storing them is not enough. Salted hashes are the prescribed way of defeating rainbow tables.
A salt is a random value that is generated and added to the password provided by the user before hashing the value and storing the hash in the database. This bit of randomness ensures that an attacker would have to precompute all hashes for all possible combinations of salt and password value, which is quite difficult.
Salts should be generated by using a cryptographically secure random number generator. This ensures that even if two users provide the same password, their hashes will be different. See how this works in the example below:
That bit of randomness added to the password will further protect all of your users in the case of a data breach.
Password-based key derivation functions
Another great tool developers can use to protect their passwords is a password-based key derivation function, or PBKDF. PBKDFs are great tools for two reasons:
- They make it practically impossible to figure out passwords using rainbow tables.
- They make it really hard to brute force passwords because they deliberately slow down the hash calculation.
PBKDFs take three inputs: the password you want to hash, the salt, and a number of iterations to perform on the password to get the final hash.
A huge advantage of PBKDFs is that they are configurable by nature. As processors get faster and better at cracking passwords, you can simply increase the number of iterations in order to stay ahead of the bad guys.
Keep in mind that there are tradeoffs associated with this method. Increasing the number of iterations will also increase processing time. This is a great defense against brute force attacks, but will mean longer processing times for users if you are using PBKDFs for the user authentication process.
You should find a balance between security and usability, as is often the case. I suggest 50,000 iterations as a baseline, increasing as you can tolerate the performance impact.
How to Use .NET to Protect Your Passwords
Now that you understand the high-level concepts that can be used with any technology, let’s take a look at how .NET enables developers to protect passwords within their systems.
If you want good cryptography using the .NET framework, then you should get familiar with the System.Security.Cryptography namespace. Many of the tools we will discuss here are in that namespace. Let’s begin.
The first order of business when you are implementing cryptography is choosing the correct random number generator. Random number generators will be the source of entropy for your keys in encryption and salts for hashes. We’ll concentrate on how to create a salt with a random number generator, but the same technique would work for creating a key as well.
For secure random numbers in .NET, use the RNGCryptoServiceProvider to generate a random set of bytes that you can then use as your salt. Do not use System.Random to generate secure random numbers. System.Random was not created with security in mind, and thus has a repeatable and guessable pattern in its generation of random numbers. RNGCryptoServiceProvider will give you the entropy you need for a secure salt (or key).
You use RNGCryptoServiceProvider by creating an instance and using the GetBytes method to generate the random set of bytes. RNGCryptoServiceProvider implements IDisposable, so make sure you wrap it in a using statement to make sure the object is properly disposed of when you are done with it.
Below is an example of using the RNGCryptoServiceProvider to securely generate a salt:
It’s only a few lines of code, but it can make a world of difference in the strength of your encryption and hashes. Wrap up the RNGCryptoServiceProvider in a library so that all of your developers can use it for all applications.
Hashing passwords with .NET
The System.Security.Cryptography has many classes that implement common encryption and hashing algorithms. It would be wise to become familiar with them. The classes available to you are simply named after the algorithm you want to use. There is the MD5 class, the SHA1, SHA256, and SHA512 classes, and so on.
SHA256 and SHA512 are your best bets for secure hashing (along with a random salt). Using them is actually refreshingly simple. You instantiate an object by calling the static Create() method on the class you want to use and then call ComputeHash() method to get the hash. It looks like this:
If you wish to use a PBKDF for storage of passwords, the .NET framework provides a class called rfc2898DeriveBytes (named after the RFC of the specific PBKDF used). It’s kind of a strange name, but the usage is still pretty simple:
An often overlooked security feature available in .NET is the SecureString class in the System.Security namespace. The SecureString class provides a secure alternative to the base String class when dealing with sensitive data such as passwords or credit card numbers. Let’s take a look at what the differences are between these two classes.
|Multiple copies in memory||One copy in memory|
|In memory unencrypted||Encrypted in memory|
|No easy way to zero out||Implements IDisposable and a Clear() method|
|Immutable, so extra are created and kept in memory when changed||MakeReadOnly() method to prevent any changes to the string|
The SecureString should not be used everywhere, but its use should be considered when dealing with any sensitive data in memory.
I stumbled upon an interesting library called CryptSharp. It offers many great tools and cryptographic functions that could definitely come in handy if you don’t want to roll your own tools such as salt generators. It also provides you with the ability to use any hash algorithm with a PBKDF, as the native .NET implementation only supports SHA-1. It’s definitely worth a look if you are serious about securing your passwords.
The last .NET framework piece I’ll mention is ASP.NET Identity. This library is included in the project templates for ASP.NET applications in Visual Studio, and is meant to provide a clean interface for developers to manage identities without rolling their own identity management solutions.
The secure storage of passwords is abstracted away, which can be good or bad depending on your opinion of such things. The full scope of functionality could be a post unto itself. The best place to get started would be on the official docs site.
The Final Dos and Don’ts of Password Storage
I know all this information is undoubtedly a lot to take in. But here are the things I want you to remember:
- Never store passwords in cleartext, anywhere.
- Just hashing is not enough, as rainbow tables can easily break through them.
- Salt your passwords with random data before hashing them to strengthen the hashes.
- The best protection against password attacks is to use a PBKDF, but remember that there is a performance cost.
- The System.Security.Cryptography namespace holds hash implementations and a PBDKF implementation for your use.
- Don’t use System.Random for random numbers. Instead, use RNGCryptoServiceProvider.
The thought of storing passwords doesn’t have to be scary. Understanding best practices is the first step. From there, you can use good judgement, always keeping your users’ best interests at heart. Armed with both knowledge and empathy, you can keep your users safe and your passwords secure.