Security Briefs

Hashing Passwords, The AllowPartiallyTrustedCallers Attribute

Keith Brown

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

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:

<users> <user name='Alice' password='7&y2si(V1dX'/> <user name='Bob' password='mary'/> <user name='Fred' password='mary'/> </users>

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:

<users> <user name='Alice' password='7&y2si(V1dX'/> <user name='Bob' password='mary'/> <user name='Fred' password='mary'/> </users>

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:

<users> <user name='Alice' password='D16E9B18FA038...'/> <user name='Bob' password='5665331B9B819...'/> <user name='Fred' password='5665331B9B819...'/> </users>

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:

<users> <user name='Alice' salt='Tu72*&' password='6DB80AE7...'/> <user name='Bob' salt='N5sb#X' password='096B1085...'/> <user name='Fred' salt='q-V3bi' password='9118812E...'/> </users>

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

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);

Figure 2 SaltedHash Class

namespace DevelopMentor.SecUtils { using System; using System.Security.Cryptography; public sealed class SaltedHash { public string Salt { get { return _salt; } } public string Hash { get { return _hash; } } public static SaltedHash Create(string password) { string salt = _createSalt(); string hash = _calculateHash(salt, password); return new SaltedHash(salt, hash); } public static SaltedHash Create(string salt, string hash) { return new SaltedHash(salt, hash); } public bool Verify(string password) { string h = _calculateHash(_salt, password); return _hash.Equals(h); } private SaltedHash(string s, string h) { _salt = s; _hash = h; } private static string _createSalt() { byte[] r = _createRandomBytes(saltLength); return Convert.ToBase64String(r); } private static byte[] _createRandomBytes(int len) { byte[] r = new byte[len]; new RNGCryptoServiceProvider().GetBytes(r); return r; } private static string _calculateHash(string salt, string password) { byte[] data = _toByteArray(salt + password); byte[] hash = _calculateHash(data); return Convert.ToBase64String(hash); } private static byte[] _calculateHash(byte[] data) { return new SHA1CryptoServiceProvider().ComputeHash(data); } private static byte[] _toByteArray(string s) { return System.Text.Encoding.UTF8.GetBytes(s); } private readonly string _salt; private readonly string _hash; private const int saltLength = 6; } }

Q When should I use the AllowPartiallyTrustedCallers attribute in my assembly?

Q When should I use the AllowPartiallyTrustedCallers attribute in my assembly?

A Only after careful review of your code. This attribute was added very late in the development of version 1.0 of the Microsoft® .NET Framework. It wasn't in the beta. In fact, it was so close to the 1.0 release that it didn't make it into the mainstream documentation, but you could read a bit about it in the release notes for the product. Let me first explain the motivation behind its existence.

A Only after careful review of your code. This attribute was added very late in the development of version 1.0 of the Microsoft® .NET Framework. It wasn't in the beta. In fact, it was so close to the 1.0 release that it didn't make it into the mainstream documentation, but you could read a bit about it in the release notes for the product. Let me first explain the motivation behind its existence.

When you build strongly named assemblies, those assemblies may be placed in the Global Assembly Cache (GAC), which means they can be seen not only by your code, but by any other code on the machine, including mobile code that may have originated from an untrusted source. If you assume your code might have bugs in it, and if you also assume that those bugs could lead to security problems if exploited by malicious code, then you'll be happy to know that your assembly can only be called by fully trusted assemblies by default. This means that random code downloaded from the Internet won't be able to call your code by default because the downloaded code is only partially trusted.

Figure 3 shows an example of a very buggy piece of code that can be exploited by an attacker. Every line of this code has a bug. The Assert statement prevents a stack walk that would keep partially trusted code from accessing the database. The database connection string uses the sa account, which if misused could severely damage or destroy the database. The SQL statement is concatenated so that it incorporates unfiltered external input in the form of a name and a password. This constitutes a SQL injection vulnerability, as an attacker could use the name and password to inject malicious SQL statements! There are so many things wrong with this code, and yet sadly it's typical code that you'd see in a database programming example. If an unsuspecting programmer cuts and pastes code like this into his source file, he's opening security holes. A code audit would catch these bugs, but code is frequently shipped without proper security testing.

Figure 3 Do Not Use

public class HopeThisIsntUsedByEvilCodeBecauseItsNotRobust { public string Name; public string Password; // this entire function consists of horribly bad code // you should NEVER EVER use public bool IsValidUser() { new DbDataPermission(PermissionState.Unrestricted).Assert(); SqlConnection conn = new SqlConnection( "initial catalog=accounts;user id=sa;password=;") conn.Open(); cmd.CommandText = string.Format( "select count(*) from users where name='{0}'" + "and password='{1}'", Name, Password); return ((int)cmd.ExecuteScalar()) > 0; } }

This is where AllowPartiallyTrustedCallers comes in. Microsoft decided at the last minute to add a final knob that had to be turned before partially trusted code would be allowed to call your locally installed, fully trusted code. Here's the deal: without this attribute on your strongly named assembly, there is effectively a link-time demand in your assembly that prevents partially trusted code from linking to it.

Now, hopefully the answer is clearer. The only time you should ever add the AllowPartiallyTrustedCallers attribute to your assembly is after a careful security audit. Be doubly wary if your assembly calls unmanaged code. As soon as you apply this attribute to a GAC-deployed assembly, you're opening that assembly up to attack from external untrusted code. Even the core .NET assemblies don't all have this attribute. For example, partially trusted code isn't allowed to use System.Runtime.Remoting.dll or System.Management.dll. The reason is that Microsoft apparently hasn't convinced itself that partially trusted code can't use these libraries to obtain elevated privileges. Only recently did ASP.NET pass examination and receive this attribute: System.Web.dll was awarded the AllowPartiallyTrustedCallers attribute in version 1.1 of the Microsoft .NET Framework.

Send your questions or comments for Keith to  briefs@microsoft.com.

Keith Brown is a member of the technical staff at DevelopMentor, where he spends his time researching, writing, teaching, and generally spreading the word about security to the developer community. He is the author of Programming Windows Security (Addison Wesley, 2000) and is working on a new book on .NET Security. Read chapters of the new book online and reach Keith at https://www.develop.com/kbrown.