|
||
|
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 |
|
|
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 hashs strength isnt 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, Netscapes 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 youre 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 dont 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.
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 Publishings 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. |

