Monday, May 24, 2010

Hashing Passwords, The AllowPartiallyTrustedCallers Attribute

Hashing Passwords, The AllowPartiallyTrustedCallers Attribute: "Hashing Passwords, The AllowPartiallyTrustedCallers Attribute
Keith Brown
Hashing Passwords, The AllowPartiallyTrustedCallers Attribute
Keith Brown

Code download available at: SecurityBriefs0308.exe (110 KB)
Browse the Code Online

Q How can I store passwords in a custom user database?
Q How can I store passwords in a custom user database?

A There are several options. The simplest might leave you with cleartext passwords. The following example is XML, but you could easily use a database table:







After implementing something like this, you'll likely feel rather uncomfortable that all those passwords are sitting there in one file, in the clear. If you don't feel uncomfortable, you should! This makes it way too easy for an attacker who compromises your system to walk away with user passwords without even breaking a sweat. And, if this happens, it's not just your site that could feel the repercussions—most people use the same password for multiple sites. A stolen password is a privacy violation for the user, and frankly, if you didn't do anything to protect those passwords, you're to blame.
A There are several options. The simplest might leave you with cleartext passwords. The following example is XML, but you could easily use a database table:







After implementing something like this, you'll likely feel rather uncomfortable that all those passwords are sitting there in one file, in the clear. If you don't feel uncomfortable, you should! This makes it way too easy for an attacker who compromises your system to walk away with user passwords without even breaking a sweat. And, if this happens, it's not just your site that could feel the repercussions—most people use the same password for multiple sites. A stolen password is a privacy violation for the user, and frankly, if you didn't do anything to protect those passwords, you're to blame.
The first approach you might take to protect these passwords is to encrypt them. That's better than nothing, but it's not the best solution either. In order to validate a user's password, you need the encryption key, which means it needs to be available on the machine where the passwords are processed. While this does raise the bar a bit because the attacker must find the key, there's a better solution that doesn't require any key at all: a one-way function.
A cryptographic hash algorithm like SHA-1 or MD5 is a sophisticated one-way function that takes some input and produces a hash value as output, like a checksum, but more resistant to collisions. This means that it's incredibly unlikely that you'd find two messages that hash to the same value. In any case, because a hash is a one-way function, it can't be reversed. There is no key that you need to bury. So let's imagine you hash the password before storing it in the database:







Now when you receive the cleartext password and need to verify it, you don't decrypt the stored password for comparison. Instead, you hash the password provided by the user and compare the result with your stored hash. If an attacker manages to steal your password database, he won't immediately be able to use the passwords, as they can't be reversed back into cleartext. But look closely at Bob and Fred's hashed passwords. If the attacker happened to be Fred, he now knows that Bob uses the same password he does. What luck! Even without this sort of luck, a bad guy can perform a dictionary attack against the hashed passwords to find matches.
The usual way a dictionary attack is performed is to get a list of commonly used passwords, like the lists you'll find at ftp://coast.cs.purdue.edu/pub/dict/wordlists, and calculate the hash for each. Now the attacker can compare the hash values of his dictionary with those in the password database. Once he finds a match, he looks up the corresponding password.
To slow down the attack, use salt. Salt is a way to season the passwords before hashing them, making the attacker's precomputed dictionary useless. Here's how it's done. Whenever you add an entry to the database, you calculate a random string of digits to be used as salt. When you want to calculate the hash of Alice's password, you look up the salt value for Alice's account, prepend it to the password, and hash them together. The resulting database looks like this:







Note that now there is no way to tell that Bob and Fred are using the same password. Note that the salt itself isn't a secret. The important thing is that it's different for each user account, so generating a random string of digits based on output from RNGCryptoServiceProvider would work fine.
Figure 1 summarizes the levels of security. Once you decide to store hashed passwords, you'll realize there's no way to e-mail the user their password if they forget what it is. This is a good thing! It's pretty silly to e-mail passwords. Take a lesson from paypal.com. They store a set of questions and answers, like "What is your pet's name?" and "What city were you born in?" To change your password, you must provide answers to these types of questions.
Figure 1 Levels of Security
With a salted password database, the attacker can't use a prehashed dictionary. But he can still perform a dictionary attack using the salt for each account to rehash his dictionary. He can also try to find short passwords with brute-force by calculating the hash of, say, every possible four-character password. You can slow this attack by requiring a certain level of complexity for passwords, including a minimum length. You can also require that users use a combination of uppercase and lowercase letters, digits, and punctuation. Of course, if passwords are too hard to remember, users may write them down. It's a difficult balancing act.
Salt isn't a silver bullet. You should react immediately if your password database has been compromised, even if it uses salted hashes. But salt buys you a wee bit of extra time. It just might give you enough time to discover the attack and disable the affected accounts until the users can change their passwords.
To make it easy for you to get started with this technique, Figure 2 provides a class in C# that will verify passwords using salted hashes. It will also create the salt and calculate the hash for new passwords as new accounts are added to your database. You just need to provide storage for the salt and the password hash, which are both strings. Here's an example of usage:

string password = Console.ReadLine();
SaltedHash sh = SaltedHash.Create(password);

// imagine storing the salt and hash in a database
string salt = sh.Salt;
string hash = sh.Hash;

Console.WriteLine("Salt: {0}", salt);
Console.WriteLine("Hash: {0}", hash);

// after looking up salt and hash, verify a password
SaltedHash ver = SaltedHash.Create(salt, hash);
bool isValid = ver.Verify(password);

No comments:

Post a Comment