Team LiB
Previous Section Next Section

Data Security in ASP.NET Applications

You've seen how to add code to the existing security framework to add enhanced role-based security and other features. All of that won't do you much good if your website can't protect your data and your users' data. The next section discusses various things you can do to secure the data on which your site operates as well as the data that belongs to your users, such as sensitive information like credit card data and passwords.

Protecting Connection Strings and Web.config Data

Everything that resides in your Web.config file is human-readable text. That means that if anyone were to ever gain possession of your Web.config file, he would be able to read all the sensitive information contained therein.

In theory, the information contained in your Web.config file should do an attacker no good. For example, if he gains access to your database connection string, he should be physically unable to connect to your database, right? Wrong. Even if the database is in a secure zone, it is still possible to hijack the web server and make it do things that ordinary users cannot do, such as connect to a database.

A common dilemma is how to keep information in the Web.config file where it is most useful to the application, but make that information unreadable to anyone but the application.

There are two options. The first option is the most obvious: don't actually store your sensitive information in the Web.config file. Some security experts recommend not only encrypting the connection string, but also storing that encrypted connection string in the Registry rather than in the Web.config file. The second option is to encrypt the string data that you are storing in Web.config and then have your application perform the decryption once at run-time and cache the decrypted value.

The code for encrypting and decrypting a string was covered in depth in Chapter 35, "Securing Sensitive Data."

Protecting User Passwords

User passwords are some of the most important pieces of data that you can house within your website. Anyone who gains access to someone else's password gains access to all the information about that user within the website. With a stolen password, people can buy items on someone else's credit card and commit everything from fraud to identity theft.

For this reason, every website that maintains user passwords needs to be able to assure the users of that website that the passwords are secure and stored in such a way that they cannot be compromised.

The best way to do this is to store the user passwords in such a way that you are not actually storing the password. Instead, you are storing a hash of the password. The hash is the result of a mathematical encryption calculation that reduces (or hashes) a password or any other string into a fixed-length representation. The same string will always reduce to the same hash and it is mathematically impossible for two different strings to produce different hashes.

Although you can use the standard .NET encryption library for producing the hashes of user passwords, there is a far easier method. The FormsAuthentication utility class provides a method called HashPasswordForStoringInConfigFile. Even though the method name is pretty lengthy, it's actually pretty easy to use. The following is a demonstration of the syntax:

string pwdHash =
  FormsAuthentication.HashPasswordForStoringInConfigFile(
    password, "SHA1");

The SHA1 algorithm produces a 160-bit digest (20 ASCII characters), whereas the MD5 algorithm produces a digest that is 128 bits (16 ASCII characters).

The idea here is to accept a password from a user in some secure manner (such as via SSL) and then create a hash of that password to store in your database. When the user supplies a password to log in, that password is hashed and then compared to the value stored in the database. In this way, the comparison can be done without sending a nonencrypted password over any wire. A sample ValidateUser method is shown here:

public bool ValidateUser( string userName, string password )
{
   string userHash =
    FormsAuthentication.HashPasswordForStoringInConfigFile(
      password, "SHA1" );
   string storedHash = // get from database
   return (userHash == storedHash);
}

All you need to do is store hashed passwords in your user database and you can be sure that no one will ever be able to hack your passwords off the wire or get into your user database, right? Wrong.

Remember the nature of hashes: When you hash a string, it will always return the same hash. That means two identical strings return identical hashes. This fact is an invitation for a hacker. A hacker can easily use the same hashing mechanism to hash a large dictionary and then start comparing hashes against hashes obtained from your website. Even easier is if the hacker finds two users with the same password. Both have the same hash and that will be the first step toward compromising your user data. Remember that a hacker doesn't need to break into your website to get a list of hashed passwords. Any site using the same hashing technique that is vulnerable could easily supply a hacker with a reference database he needs to hack yours.

The solution to this is to "salt" your hash. Although that sounds appetizing, it actually refers to adding an additional bit of information to the user's password before it is hashed. This way you can guarantee two things:

  • No two users will ever have the same passwords.

  • No password will ever match any entry from any language dictionary.

The way to implement this is to first come up with the unique salt for the user's password hash. Then concatenate the salt onto the user's password and hash the password. When storing the password in your database, store the salt value along with it so that you can properly hash that user's password with the attempt to authenticate against your website.

There are two very good ways of coming up with unique salts. The first is to create a new GUID when you create a new user, and use that GUID as the user's hash salt. The second is to use the RNGCryptoProvider (RNG in this case stands for random number generator) to come up with a "more random" random number sequence than the operating system can normally provide.

Many developers prefer to use random number sequences because everyone knows how long GUIDs are and they are easily recognizable as GUIDs. A random number sequence can be any length and you can easily disguise it as some value that might or might not be related to passwords.

The following code snippet performs user validation when a salt has been stored in the database alongside the user's hashed password:

public bool ValidateUser(string userName, string password)
{
  string storedHash = // get from database
  string salt = // get from database
  string pwdHash = FormsAuthentication.HashPasswordForStoringInConfigFile(
    salt + password, "SHA1" );

  return (pwdHash == storedHash);
}

The thing to remember is that just because something is encrypted when it is transmitted over the wire (via SSL or something else), that doesn't mean that it is completely secure. If your database is compromised, you want to make it as hard as possible for someone to figure out the passwords. Also, you want to make sure that the password hashes from your website are never the same as the hashes from another website. There are many .NET websites appearing, and many people are making security mistakes with those websites. After reading the information in this chapter, you won't make those same mistakes.

Deciding When to Use SSL

SSL is an encryption algorithm that protects data being sent over the wire via the HTTPS protocol. When someone pulls up a URL for a website via SSL, the contents of that entire page are encrypted. Everything sent on that port comes across encrypted, including all text, all images, all scripts, everything.

For this reason, SSL often incurs a performance overhead both on the server and on the client. With broadband clients, the speed difference isn't quite as visible as it is with dial-up, but it is still a good idea to use SSL only when you absolutely have to.

The following is a quick checklist of reasons why you should use SSL. There are obviously more and every case is different, but these guidelines may help:

  • You require that the information communicated between client and server must be secure for confidentiality reasons, such as transmission of medical or financial data.

  • It is a login page and requires the user to enter her password.

  • It is a checkout or shopping cart page and requires the user to enter credit card information.

Data Security with ViewState Encryption

When ASP.NET creates the hidden form field for the page's ViewState contents, it can be instructed to store the contents in an encrypted form to prevent people from tampering with your site by examining the contents of ViewState.

By setting the validationKey attribute on the <machineKey> element to 3Des, you can provide a symmetric encryption algorithm by which all ViewState for all sites on that server are validated.

As you saw in the discussion of web farm scenarios in Chapter 25, "Advanced ASP.NET," if you want a farm to work properly, you must specify the same validation key for each server. This is to allow one server to decrypt the ViewState generated by another server.

    Team LiB
    Previous Section Next Section