Symantec ThreatCon
Nov 15 2005 12:30AM
Symantec ThreatCon
Search: Home Bugtraq Vulnerabilities Mailing Lists Security Jobs Tools
(page 6 of 7 ) previous  next 
Hacking the Code: ASP.NET Web Application Security


By Mark Burnett, James C. Foster
Published by Syngress
ISBN: 1932266658   Buy Now!
Published:April, 2004
Pages:472

 About the author
 Buy the book

Hashing Passwords

Another important use for hashes is storing passwords. As described in Chapter 1, you should not store actual passwords in your database. Using hashing algorithms, you can store the hash and use that to authenticate the user. Because it is highly unlikely that two passwords would produce the same hash, you can compare the stored hash with a hash of the password submitted by the user. If the two match, you can be sure that the user has the correct password.

Protecting passwords with hashes has some unique problems. First, although hashes are not reversible, they are crackable using a brute-force method. You cannot produce the password from the hash, but you can create hashes of millions of passwords until you find one that matches. For this reason, the hash’s strength isn’t based so much on the key length of the hashing algorithm, but on the length of the password itself. And because passwords have such low entropy, are predictable, and are often too short, this usually is not a difficult task.

Another problem with hashes is that the same data will always produce the same hash. This can be a problem if someone ever obtains the hashes, because they can use a precomputed dictionary of hashes to instantly discover common passwords. To prevent this situation, we can add a salt to the password to ensure a different hash each time. The salt should be a large random number uniquely generated for that purpose. You do not need to keep the salt private, so you can save the salt with the hash itself.

When you use a salt, there are as many possible hashes for any given piece of data as there are bits in the salt. Of course, if the intruder has access to the hashes, they also have access to the salts, but the key here is to force the attacker to compute each hash individually and not gain any benefit from passwords he or she has already cracked. Figures 4.19 and 4.20 show hashing algorithms that include salts.

Figure 4.19 Hashing with a Salt: C#

private void hashExample()

{

string plainText = "This is a secret for hashExample";

Response.Write("plainText: " + plainText + "
");

// Create strong random byte values

byte[] saltBytes = new byte[8];

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

rng.GetNonZeroBytes(saltBytes);

// Create an array to hold plain text and salt

byte[] plainTextBytes = ASCIIEncoding.ASCII.GetBytes(plainText);

byte[] plainTextAndSaltBytes =

new byte[plainTextBytes.Length + saltBytes.Length];

// Append salt value and create byte array for hash

for (int x=0; x < saltBytes.Length; x++)

plainTextAndSaltBytes[x] = saltBytes[x];

for (int x=0; x < plainTextBytes.Length; x++)

plainTextAndSaltBytes[saltBytes.Length + x] =

plainTextBytes[x];

// Perform hash using SHA1

HashAlgorithm hash = new SHA1Managed();

byte[] hashBytes = hash.ComputeHash(plainTextAndSaltBytes);

Response.Write("Hashed string with salt: " +

Convert.ToBase64String(hashBytes) + "
");

}

Figure 4.20 Hashing with a Salt: VB.NET

Private Function hashExample()

Dim plainText As String = "This is a secret for hashExample"

Response.Write("plainText: " + plainText + "
")

' Create strong random byte values

Dim saltBytes() As Byte = New Byte(8) {}

Dim rng As RNGCryptoServiceProvider = New RNGCryptoServiceProvider

rng.GetNonZeroBytes(saltBytes)

' Create an array to hold plain text and salt

Dim plainTextBytes As Byte() = ASCIIEncoding.ASCII.GetBytes(plainText)

Dim plainTextAndSaltBytes As Byte() = _

New Byte(plainTextBytes.Length + saltBytes.Length) {}

' Append salt value and create byte array for hash

Dim x As Integer

For x = 0 To saltBytes.Length - 1 Step x + 1

plainTextAndSaltBytes(x) = saltBytes(x)

Next

For x = 0 To plainTextBytes.Length - 1 Step x + 1

plainTextAndSaltBytes(saltBytes.Length + x) = plainTextBytes(x)

Next

' Perform hash using SHA1

Dim hash As HashAlgorithm = New SHA1Managed

Dim hashBytes As Byte() = hash.ComputeHash(plainTextAndSaltBytes)

Response.Write("Hashed string with salt: " + _

Convert.ToBase64String(hashBytes) + "
")

End Function

You might think that a salt is similar to an IV. In fact, it is essentially the same technique that accomplishes the same purpose. Note that it is also similar in function to a keyed hash algorithm, and a keyed function such as HMACSHA1 is an excellent replacement for the code in Figure 4.20. To use a keyed hash, simply use the salt in place of the key, and otherwise follow the sample code in Figure 4.19.

Security Policy

  • Use hashing algorithms to verify integrity and store passwords.
  • For data verification, you can allow others to view a hash, but you must protect it from being modified.
  • Use keyed hashing algorithms to protect the hash from being modified.
  • For password authentication, keep the hashes secret to prevent brute-force attacks.
  • Add salt to a hash to ensure randomness.

Working with .NET Encryption Features

Now that you understand the basics of .NET encryption, we need to examine some topics related to using these features. In this section, we will cover:

  • Creating random numbers
  • Keeping memory clean
  • Protecting secrets
  • Protecting communications with SSL

Creating Random Numbers

Summary:

Random numbers are a key part of cryptography, but it is nearly impossible for a computer to be completely random on its own

Threats:

Information leakage, data corruption, man-in-the-middle attacks, brute-force attacks

Random numbers are a key element of most forms of cryptography. At some point, the strength of the system is based on the ability to produce a random, unpredictable number. Without this randomness, an attacker might be able to predict the cryptographic calculations. For example, Netscape’s SSL encryption was cracked in 1995 because it used a weak random-number generator.

Because computers have difficulty with true randomness, they use what are called pseudorandom-number generators (PRNGs). To be used with cryptography, a PRNG should have good entropy. Entropy is the measure of randomness that refers to the unpredictability and the uniform distribution of a random number. A strong random-number generator is one that, given a list of numbers already generated, could not predict the next number in the sequence.

The .NET Framework uses the RNGCryptoServiceProvider to generate random numbers, which is a wrapper for the CryptGenRandom function in CrytpoAPI. This PRNG is considered random enough for all but the most extreme security requirements. If you want to implement even stronger random-number generators, check out www.schneier.com/yarrow.html and www.irisa.fr/caps/projects/hipsor/HAVEGE.html. Note that CryptoAPI gets its entropy from a surprisingly large number of system factors, but if you want to add further entropy, you can pass a string or byte array of entropy when creating the class.

Warning

When you’re using random numbers for cryptographic purposes, always use RNGCryptoServiceProvider, not the System.Random class. System.Random is based on a predictable function and is not considered a strong random-number generator.

Security Policy

  • Use only RNGCryptoServiceProvider to generate strong random numbers; avoid using System.Random.
  • Use external sources of entropy to further increase randomness of the PRNG.

Keeping Memory Clean

Summary:

You must carefully plan your code to prevent leaving secrets exposed in memory

Threats:

Information leakage

When working with sensitive data, you should always make sure you clean up after yourself; you don’t want unencrypted data left around in memory. To limit the possibility of leaving sensitive data in memory, you should use as few variables as possible, avoid caching plaintext, and explicitly clean up after cryptographic operations.

Normally the .NET Framework garbage collector will take care of reclaiming objects no longer used. But it is not predictable when garbage collection will occur, and when it does, the system does not actually clear the memory; it just marks it as available for reuse. Because that memory may contain important information, it is important to always clean up any cryptographic objects and variables. To do this, the .NET Framework provides a Clear() method for all cryptographic objects.

When you are finished with any cryptography-related variables, you should clear their contents. This includes not only ciphertext and plaintext variables but also crypto objects, keys, salt, and IV variables. Figures 4.21 and 4.22 show examples of how to do this.

Figure 4.21 Clearing Crypto-Related Objects: C#

private void cleaningExample()

{

// Setup

byte[] buffer = ASCIIEncoding.ASCII.GetBytes("This is a secret for cleaningExample");

RijndaelManaged rijndael = new RijndaelManaged();

rijndael.GenerateKey();

rijndael.GenerateIV();

// Perform encryption

string encryptedString = Convert.ToBase64String(

rijndael.CreateEncryptor().TransformFinalBlock(

buffer, 0, buffer.Length));

// Perform decryption

buffer = Convert.FromBase64String(encryptedString);

string decryptedString = ASCIIEncoding.ASCII.GetString(

rijndael.CreateDecryptor().TransformFinalBlock(

buffer, 0, buffer.Length));

// Clean up

rijndael.Clear();

encryptedString = decryptedString = String.Empty;

buffer = null;

}

Figure 4.22 Clearing Crypto-Related Objects: VB.NET

Private Sub cleaningExample()

' Setup

Dim buffer() As Byte = ASCIIEncoding.ASCII.GetBytes("This is a secret for cleaningExample")

Dim rijndael As RijndaelManaged = New RijndaelManaged

rijndael.GenerateKey()

rijndael.GenerateIV()

' Perform encryption

Dim encryptedString As String = Convert.ToBase64String( _

rijndael.CreateEncryptor().TransformFinalBlock( _

buffer, 0, buffer.Length))

' Perform decryption

buffer = Convert.FromBase64String(encryptedString)

Dim decryptedString As String = ASCIIEncoding.ASCII.GetString( _

rijndael.CreateDecryptor().TransformFinalBlock( _

buffer, 0, buffer.Length))

' Clean up

rijndael.Clear()

encryptedString = decryptedString = String.Empty

buffer = Nothing

End Sub

Note that some variables do not have Clear() methods, so we explicitly zero those out. Be sure to watch all subroutines you call to clear any variables used in those.

Security Policy

  • Clear out all variables used with cryptographic operations, including those for plaintext, ciphertext, keys, salts, IVs, and random numbers.
  • Use the Clear() method to clear out any sensitive data.
  • Use the Dispose() method to immediately free memory resources.
  • Explicitly zero out any variables that do not provide a Clear() method.

Excerpt continued on Page 7 

About the author
Mark Burnett (Microsoft MVP) is an independent security consultant, freelance writer, and a specialist in securing Windows-based IIS Web servers. Mark is co-author of Maximum Windows Security and is a contributor to Dr. Tom Shinder's ISA Server and Beyond: Real World Security Solutions for Microsoft Enterprise Networks (Syngress Publishing, ISBN: 1-931836-66-3). He is a contributor and technical editor for Syngress Publishing’s Special Ops: Host and Network Security for Microsoft, UNIX, and Oracle (ISBN: 1-931836-69-8). Mark speaks at various security conferences and has published articles in Windows & .NET, Information Security, Windows Web Solutions, Security Administrator, and is a regular contributor at SecurityFocus.com. Mark also publishes articles on his own Web site, IISSecurity.info.
(page 6 of 7 ) previous  next 







 

Privacy Statement
Copyright 2005, SecurityFocus