Hacking the Code ASP.NET Web Application Security [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Hacking the Code ASP.NET Web Application Security [Electronic resources] - نسخه متنی

James C. Foster, Mark M. Burnett

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






Using Cryptography in ASP.NET

Before you integrate cryptography into your ASP.NET application, you should first understand when to use the various available ciphers. ASP.NET has three main categories of cryptographic cipher:



Symmetric algorithms



Asymmetric algorithms



Hashing and signature algorithms



Each of these plays a unique role in the overall security of an ASP.NET application. It is important to understand which type of cipher to use in various situations. Table 4.1 shows some of the different uses for the various categories of ciphers.





















Table 4.1: Encryption Algorithms Available with the .NET Framework

Category


Algorithms


Suggested Uses


Symmetric


DES, 3DES, Rjindael (AES), RC2


Bulk data encryption


Assymetric


RSA


Authentication, data integrity


Hashing and


MD5, SHA1, SHA256, SHA384, SHA512, DSA


Data integrity






Warning

With so signatures much built-in support for well-accepted encryption algorithms, there is no excuse to try to “roll your own.” In fact, because so few people are actually qualified to write strong encryption algorithms, you should never trust your own code. Furthermore, data obfuscation algorithms such as XOR, ROT-13, and base-64 encoding provide little if any realized protection. Despite our best efforts to educate everyone on this, programmers consistently and predictably use these flawed techniques to protect secret data.



Employing Symmetric Cryptography












Summary:


Symmetric cryptography is useful for bulk encryption, but scalability and key management issues limit its use


Threats:


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


Symmetric cryptography, known also as secret key cryptography, is the use of a single shared secret to share encrypted data between parties. Ciphers in this category are called symmetric because you use the same key to encrypt and to decrypt the data. In simple terms, the sender encrypts data using a password, and the recipient must know that password to access the data.

Symmetric encryption is a two-way process. With a block of plaintext and a given key, symmetric ciphers will always produce the same ciphertext. Likewise, using that same key on that block of ciphertext will always produce the original plaintext. Symmetric encryption is useful for protecting data between parties with an established shared key and is also frequently used to store confidential data. For example, ASP.NET uses 3DES to encrypt cookie data for a forms authentication ticket.

Table 4.2 shows the characteristics of the symmetric encryption algorithms available in the .NET Framework. Although these algorithms work differently, the .NET Framework provides a standardized model through the SymmetricAlgorithm abstract base class.
























Table 4.2: .NET Framework Symmetric Encryption Algorithms

Name


Block Size


Cipher Modes


Key Lengths


DES


64


CBC, ECB, and CFB


56 bits


Triple DES (3DES)


64


CBC, ECB, and CFB


Two or three 56-bit keys


Rijndael (AES)


128, 192, 256


CBC and ECB


128, 192, or 256 bits


RC2


64


CBC, ECB, and CFB


40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, or 128 bits


In addition to providing access to different encryption algorithms, the .NET Framework also allows you to customize the cipher modes, key lengths, block sizes, and padding mode, as well as other parameters. The cipher mode determines the cipher’s mode of operation. Although the CipherMode enumeration includes five different modes, only three are supported with existing algorithms, as shown in Table 4.2. The CipherMode options are:



Electronic Codebook Mode (EBC) The simplest and fastest mode, EBC allows ciphertext to be broken one block at a time and allows for codebook compilation. Encrypted blocks can be replaced without affecting the entire message. This mode is useful only where performance is the highest priority, at the expense of security.



Cipher Block Chaining Mode (CBC) This mode uses an initialization vector (IV) to add feedback to the block transformation. This prevents the problems seen with EBC mode. Decryption requires knowing the IV, but this is not a secret and you can transmit it over an insecure connection.



Cipher Feedback Mode (CFB) Uses an IV as CBC does but works with partial blocks, making it well suited for encrypting streaming data.







Tip

Although the symmetric algorithms available with the .NET Framework are all block ciphers, you can access them through a stream-oriented design. However, they are still block ciphers, and you should not confuse them with stream ciphers that are not always as secure.


You can see a full implementation of all the symmetric algorithms and settings in action with the symmetric.aspx file found in the \Ch04 directory of the supplemental code download available at this book’s Web site (www.syngress.com/solutions). Figure 4.1 shows an example of how this page looks.


Figure 4.1: Symmetric Cryptography Sample


DES and 3DES


The U.S. government developed Data Encryption Standard (DES) in 1977 as an official cryptography standard; it is still used widely. DES formed the basis of the first automated teller machine (ATM) personal identification number (PIN) code authentication and until recent years existed as the primary authentication encryption method for UNIX machines. DES is a block cipher using a 64-bit block size with a 56-bit key length. In early 1990 it was proven insufficiently secure given current hardware capabilities and that it was possible to exhaust all potential DES key combinations in less than a day. Triple DES (also known as 3DES) emerged to address DESs shortcomings. 3DES uses standard DES encryption cycled over itself three times, with one cycle using a different set of encryption keys. This was a simple yet easy way to effectively increase the key size from 56-bit to 168-bit, thus increasing its security, but it obviously takes three times longer than DES to encrypt the data.

DES is probably nearing the end of its useful life, and 3DES is not as efficient as other algorithms, but they both still dominate as the algorithms of choice. Many programmers feel more comfortable using these algorithms because of their compatibility and wide acceptance.

The .NET Framework provides access to these algorithms through the DESCryptoServiceProvider and TripleDESCryptoServiceProvider classes. Note that both of these classes are managed wrappers that call the unmanaged Win32 CryptoAPI functions. Figures 4.2 and 4.3 demonstrate the use of 3DES encryption.







private void tripleDES() 
{
string plainText = "This is a secret for 3DES";
Response.Write("plainText: " + plainText + "<br>");
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(plainText);
TripleDESCryptoServiceProvider des =
new TripleDESCryptoServiceProvider();
des.GenerateKey();
des.GenerateIV();
string encryptedString = Convert.ToBase64String(
des.CreateEncryptor().TransformFinalBlock(
buffer, 0, buffer.Length));
Response.Write("Encrypted string: " + encryptedString + "<br>");
buffer = Convert.FromBase64String(encryptedString);
string decryptedString = ASCIIEncoding.ASCII.GetString(
des.CreateDecryptor().TransformFinalBlock(
buffer, 0, buffer.Length));
Response.Write("Decrypted string: " + decryptedString + "<br>");
}






Figure 4.2: 3DES Encryption with ASP.NET: C#







Private Function tripleDES()
Dim plainText As String = "This is a secret for 3DES"
Response.Write("plainText: " + plainText + "<br>")
Dim buffer() As Byte = ASCIIEncoding.ASCII.GetBytes(plainText)
Dim des As New TripleDESCryptoServiceProvider
des.GenerateKey()
des.GenerateIV()
Dim encryptedString As String = _
Convert.ToBase64String( _
des.CreateEncryptor().TransformFinalBlock( _
buffer, 0, buffer.Length))
Response.Write("encryptedString: " + encryptedString + "<br>")
buffer = Convert.FromBase64String(encryptedString)
Dim decryptedString As String = _
ASCIIEncoding.ASCII.GetString( _
des.CreateDecryptor().TransformFinalBlock( _
buffer, 0, buffer.Length))
Response.Write("Decrypted string: " + decryptedString + "<br>")
End Function






Figure 4.3: 3DES Encryption with ASP.NET: VB.NET





Tip

Although DES and 3DES do not use managed code, you can get some open-source implementations of these classes that contain fully managed code. Open-source implementations of the .NET Framework include the mono project (www.go-mono.com) and DotGNU Portable .NET (www.southern-storm.com.au/portable_netl). Note, however, that the native .NET Framework classes that call the CryptoAPI have been FIPS 140-1 certified (see http://csrc.nist.gov/cryptval/140-1/1401vend).


DES does have some keys that you must avoid because they are weak. In fact, there are four keys that produce the same subkeys in every round. This means that if you encrypt data with one of these keys and then encrypt that encrypted data again with the same key, you will end up with the original plaintext message. In addition to these weak keys are 12 semi-weak keys. Semi-weak keys work in pairs, where one key decrypts data encrypted with the first. ASP.NET allows you to check for these with the IsWeakKey and IsSemiWeakKey methods. You can view the actual source code for the IsWeakKey method in Figure 4.4 and the IsSemiWeakKey method in Figure 4.5.







public static bool IsWeakKey(byte[] rgbKey) {                                     
if (!IsLegalKeySize(rgbKey)) {
throw new CryptographicException(
Environment.GetResourceString(
"Cryptography_InvalidKeySize"));
}
UInt64 key = QuadWordFromBigEndian(rgbKey);
if ((key == 0x0101010101010101) ||
(key == 0xfefefefefefefefe) ||
(key == 0x1f1f1f1f0e0e0e0e) ||
(key == 0xe0e0e0e0f1f1f1f1)) {
return(true);
}
return(false);
}






Figure 4.4: .NET Framework Source Code for the IsWeakKey Method







public static bool IsSemiWeakKey(byte[] rgbKey) {       
if (!IsLegalKeySize(rgbKey)) {
throw new CryptographicException(
Environment.GetResourceString(
"Cryptography_InvalidKeySize"));
}
UInt64 key = QuadWordFromBigEndian(rgbKey);
if ((key == 0x01fe01fe01fe01fe) ||
(key == 0xfe01fe01fe01fe01) ||
(key == 0x1fe01fe00ef10ef1) ||
(key == 0xe01fe01ff10ef10e) ||
(key == 0x01e001e001f101f1) ||
(key == 0xe001e001f101f101) ||
(key == 0x1ffe1ffe0efe0efe) ||
(key == 0xfe1ffe1ffe0efe0e) ||
(key == 0x011f011f010e010e) ||
(key == 0x1f011f010e010e01) ||
(key == 0xe0fee0fef1fef1fe) ||
(key == 0xfee0fee0fef1fef1)) {
return(true);
}
return(false);
}






Figure 4.5: .NET Framework Source Code for the IsSemiWeakKey Method





Tip

You can download the source code for the .NET Framework cryptography base classes at www.gotdotnet.com/team/clr/samples/_eula_clr_cryptosrc.aspx.


Note that automatically generated keys and those from the GenerateKey method will never produce weak keys, and the chances of randomly selecting one of these are 1 in 18,014,398,509,482,000. Furthermore, the DES and TripleDES classes will throw a CryptographicException if you try to use a weak key.

Rijndael


Given that DES is reaching the end of its useful life and 3DES is really not much more than a temporary fix, many experts are looking to other algorithms. The National Institute of Standards and Technology (NIST) recently chose the Rijndael specification as its official replacement to DES. This specification, referred to as the Advanced Encryption Standard (AES), can be found at http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf. Rijndael supports larger key sizes than DES but has improved performance over 3DES. The Rijndael specification supports key sizes of 128, 192, or 256 bits.

Because of government standardization on this algorithm, it is expected to become a widely used replacement for DES, although ASP.NET still relies heavily on DES and 3DES. However, Rijndael is the default algorithm used with the SymmetricAlgorithm class and is the only symmetric algorithm that fully runs in managed code. Figures 4.6 and 4.7 show an example using Rijndael encryption in ASP.NET. Note that Rijndael encryption does not have any known weak keys and therefore does not support the IsWeakKey method.

The Rijndael cipher is the fastest and supports the largest key size of all the .NET Framework ciphers.







private void rijndael() 
{
string plainText = "This is a secret for Rijndael";
Response.Write("plainText: " + plainText + "<br>");
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(plainText);
RijndaelManaged rijndael = new RijndaelManaged();
rijndael.GenerateKey();
rijndael.GenerateIV();
string encryptedString = Convert.ToBase64String(
rijndael.CreateEncryptor().TransformFinalBlock(
buffer, 0, buffer.Length));
Response.Write("Encrypted string: " + encryptedString + "<br>");
buffer = Convert.FromBase64String(encryptedString);
string decryptedString = ASCIIEncoding.ASCII.GetString(
rijndael.CreateDecryptor().TransformFinalBlock(
buffer, 0, buffer.Length));
Response.Write("Decrypted string: " + decryptedString + "<br>");
}






Figure 4.6: Rijndael Encryption: C#








Private Function rijndaelEncryption()
Dim plainText As String = "This is a secret for Rijndael"
Response.Write("plainText: " + plainText + "<br>")
Dim buffer() As Byte = ASCIIEncoding.ASCII.GetBytes(plainText)
Dim rijndael As RijndaelManaged = New RijndaelManaged
rijndael.GenerateKey()
rijndael.GenerateIV()
Dim encryptedString As String = _
Convert.ToBase64String( _
rijndael.CreateEncryptor().TransformFinalBlock( _
buffer, 0, buffer.Length))
Response.Write("encryptedString: " + encryptedString + "<br>")
buffer = Convert.FromBase64String(encryptedString)
Dim decryptedString As String = _
ASCIIEncoding.ASCII.GetString( _
rijndael.CreateDecryptor().TransformFinalBlock( _
buffer, 0, buffer.Length))
Response.Write("Decrypted string: " + decryptedString + "<br>")
End Function






Figure 4.7: Rijndael EncryptionVB.NET


RC2


RC2 is a symmetric block cipher designed by Ronald Rivest of RSA. RSA designed RC2 as a direct replacement for DES, improving on the performance and providing a variable key size. RC2 is commonly used in S/MIME secure e-mail and is said to be two to three times as fast as DES. The complete RC2 specification is available at www.ietf.org/rfc/rfc2268.txt. Figure 4.8 and Figure 4.9 show examples using RC2 encryption. Like Rijndael, RC2 does not have any known weak keys and therefore does not support the IsWeakKey method.







private void rc2() 
{
string plainText = "This is a secret for RC2";
Response.Write("plainText: " + plainText + "<br>");
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(plainText);
RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
rc2.GenerateKey();
rc2.GenerateIV();
string encryptedString = Convert.ToBase64String(
rc2.CreateEncryptor().TransformFinalBlock(
buffer, 0, buffer.Length));
Response.Write("Encrypted string: " + encryptedString + "<br>");
buffer = Convert.FromBase64String(encryptedString);
string decryptedString = ASCIIEncoding.ASCII.GetString(
rc2.CreateDecryptor().TransformFinalBlock(
buffer, 0, buffer.Length));
Response.Write("Decrypted string: " + decryptedString + "<br>");
}






Figure 4.8: RC2 Encryption: C#







Private Function rc2Encryption()
Dim plainText As String = "This is a secret for RC2"
Response.Write("plainText: " + plainText + "<br>")
Dim buffer() As Byte = ASCIIEncoding.ASCII.GetBytes(plainText)
Dim rc2 As New RC2CryptoServiceProvider
rc2.GenerateKey()
rc2.GenerateIV()
Dim encryptedString As String = _
Convert.ToBase64String( _
rc2.CreateEncryptor().TransformFinalBlock( _
buffer, 0, buffer.Length))
Response.Write("encryptedString: " + encryptedString + "<br>")
buffer = Convert.FromBase64String(encryptedString)
Dim decryptedString As String = _
ASCIIEncoding.ASCII.GetString( _
rc2.CreateDecryptor().TransformFinalBlock( _
buffer, 0, buffer.Length))
Response.Write("Decrypted string: " + decryptedString + "<br>")
End Function






Figure 4.9: RC2 Encryption: VB.NET

RC2 is a widely used algorithm that allows a variety of key lengths, but you should be aware that security experts consider RC2 with smaller keys to be insecure. You should always use a 128-bit key, the maximum length available.


Selecting an Algorithm


Selecting a symmetric encryption algorithm is essentially a matter of key length, compatibility, performance, experience, and personal preference. It is extremely difficult to prove that an encryption algorithm is the most secure, although the failure to demonstrate vulnerabilities over time is usually good enough proof. Ultimately, the strength of the algorithm is based on the size of the key, but there is no guarantee that any particular algorithm is without flaws.





Warning

At the time of this writing, there are no known flaws with these algorithms, other than limitations on key length. However, we do know that some government agencies spend a great amount of money and effort looking for flaws in these algorithms. If such an agency were ever to discover (or already has discovered) a flaw, you can bet that would become one of their most closely guarded secrets. In fact, they would likely go to great lengths to give the impression that they have no clue of any flaws with the algorithms.


In a situation in which security is a much higher priority than performance, you could avoid exposure by layering multiple algorithms as shown in Figures 4.10 and 4.11. Because CryptoStreams allow chaining, it is a simple process to provide multiple layers of encryption.







private void layeringSymmetricCiphers() 
{
string plainText = "This is a secret for Layering Ciphers";
Response.Write("plainText: " + plainText + "<br>");
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(plainText);
MemoryStream encryptedMemoryStream = new MemoryStream();

  RijndaelManaged rijndael = new RijndaelManaged();
rijndael.GenerateKey();
rijndael.GenerateIV();

  RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
rc2.GenerateKey();
rc2.GenerateIV();
// Chain the streams together for encryption
CryptoStream cryptoStream = new CryptoStream(
new CryptoStream(encryptedMemoryStream, rc2.CreateEncryptor(),
CryptoStreamMode.Write), rijndael.CreateEncryptor(),
CryptoStreamMode.Write);

  cryptoStream.Write(buffer, 0, buffer.Length);
cryptoStream.FlushFinalBlock();
string encryptedString = Convert.ToBase64String(
encryptedMemoryStream.ToArray());
Response.Write("Encrypted string: " + encryptedString + "<br>");

  // Prepare for decryption
MemoryStream decryptedMemoryStream =
new MemoryStream(encryptedMemoryStream.ToArray());
buffer = new byte[decryptedMemoryStream.ToArray().Length];

  // Unchain the streams for decryption
cryptoStream = new CryptoStream(
new CryptoStream(decryptedMemoryStream, rc2.CreateDecryptor(),
CryptoStreamMode.Read), rijndael.CreateDecryptor(),
CryptoStreamMode.Read);

  cryptoStream.Read(buffer, 0, buffer.Length); 
string decryptedString = ASCIIEncoding.ASCII.GetString(buffer);
Response.Write("Decrypted string: " + decryptedString + "<br>");
}






Figure 4.10: Layering Symmetric Ciphers: C#








Private Function layeringSymmetricCiphers()
Dim plainText As String = "This is a secret for Layering Ciphers"
Response.Write("plainText: " + plainText + "<br>")
Dim buffer() As Byte = ASCIIEncoding.ASCII.GetBytes(plainText)
Dim encryptedMemoryStream As MemoryStream = New MemoryStream

  Dim rc2 As RC2CryptoServiceProvider = New RC2CryptoServiceProvider
rc2.GenerateKey()
rc2.GenerateIV()
Dim rijndael As RijndaelManaged = New RijndaelManaged
rijndael.GenerateKey()
rijndael.GenerateIV()

  ' Chain the streams together for encryption
Dim cryptoStream As CryptoStream = New CryptoStream( _
New CryptoStream(encryptedMemoryStream, rc2.CreateEncryptor, _
CryptoStreamMode.Write), rijndael.CreateEncryptor, _
CryptoStreamMode.Write)

  cryptoStream.Write(buffer, 0, buffer.Length)
cryptoStream.FlushFinalBlock()
Dim encryptedString As String = Convert.ToBase64String( _
encryptedMemoryStream.ToArray())
Response.Write("Encrypted string: " + encryptedString + "<br>")

  ' Prepare for decryption
Dim decryptedMemoryStream As MemoryStream = _
New MemoryStream(encryptedMemoryStream.ToArray())

  ' Unchain the streams for decryption
cryptoStream = New CryptoStream( _
New CryptoStream(decryptedMemoryStream, rc2.CreateDecryptor(), _
CryptoStreamMode.Read), rijndael.CreateDecryptor(), _
CryptoStreamMode.Read)

  cryptoStream.Read(buffer, 0, buffer.Length)
Dim decryptedString As String = ASCIIEncoding.ASCII.GetString(buffer)
Response.Write("Decrypted string: " + decryptedString + "<br>")
End Function






Figure 4.11: Layering Symmetric Ciphers: VB.NET


Establishing Keys and Initialization Vectors


A number of parameters determine the outcome of the ciphertext. To decrypt the ciphertext, you must use the same algorithm and the same parameters. Two of the parameters you should intentionally change each time are the key and the initialization vector (IV). The key is the secret vital to ensuring the integrity of the data and the IV ensures randomness and uniqueness of the ciphertext blocks.

If you encrypt data with the same key each time, you will always end up with the same ciphertext. Knowing this, an attacker can eventually gain enough data to decode many messages. To prevent this, the symmetric encryption algorithms use the IV to initialize the process, ensuring a unique ciphertext message. The message recipient must know both the key and the IV to decrypt the message, but only the key must remain secret.





Warning

When creating a key and IV, never derive one from the other, because knowing the IV could allow the attacker to determine the key. Also be sure to avoid a fixed IV for all encryption. The best solution is to use the random IV that the algorithm automatically creates when it’s initialized.


There are some difficulties when it comes to exchanging keys, especially when it comes to sharing a key without any prior shared secrets. The whole reason for the encryption is that you do not trust the transmission medium. Therefore, you must somehow transmit the key over an insecure connection, but if you already have a secure connection, why would you need further encryption? Suppose, for example, that you want to send someone an encrypted message. The recipient will not be able to read your message unless you give her the proper key. It makes no sense to send the key along with the message, so you instead call the recipient on the phone to convey the key. But since you already trust the phone line enough to share the key, you might as well go ahead and share the whole message. This is a major shortcoming of symmetric cryptography but is an issue we can overcome with key exchange algorithms and by using asymmetric cryptography. For an ASP.NET application, this is not as great an issue, because you can easily use SSL to establish a secure session.

Most often you will use symmetric encryption for saving sensitive settings or user data. The problem with this is that your ASP.NET application must know the key and therefore must save the key for its own use. This is a problem because if an attacker takes over the application, the attacker will gain access to the application’s keys. For example, ASP.NET uses the machine.config file to store the encryption keys for many encryption operations, such as encrypting a forms authentication ticket. If an attacker were able to read this file, that attacker could forge his own authentication tickets. To help this situation, you can use DPAPI, as explained later in this chapter. You should also design your application so that it allows you to regularly change your keys.

Sometimes you might want the user to be able to encrypt information that even you cannot access. The user provides the key and then gains access to the encrypted data. However, it normally is not practical to expect a user to memorize or type a large encryption key. If users have issues remembering a password of six or eight characters, how will they remember a 128-bit key? The solution is to allow the user to enter a password that you use to derive an appropriate key.

The PasswordDeriveBytes.CryptDeriveKey method can produce an appropriate key based on a password, salt, algorithm, and number of iterations. This method creates a hash of the password using the supplied salt and uses that hash to create another hash, repeating this process for as many iterations specified. The result is a long string suitable to use as a key. Figures 4.12 and 4.13 demonstrate the code to use CryptDeriveKey, and Figure 4.14 shows an example key derived from a user’s password.







private void cryptDeriveKeyExample() 
{
// set initial values
string password = "ThisIsMyPassword";
string salt = "ThisIsMySalt";
string algname = "RC2";
string hashName = "MD5";
byte[] IV = new byte[8];
int keySize = 128;

  // Create and PasswordDeriveBytes
PasswordDeriveBytes pdb =
new PasswordDeriveBytes(password,
ASCIIEncoding.ASCII.GetBytes(salt));

   
// Extract Key
byte[] key = pdb.CryptDeriveKey(algname, hashName, keySize, IV);

  Response.Write("derived key: " + Convert.ToBase64String(key) + "<br>");
}






Figure 4.12: Using CryptDeriveKey: C#








Private Function cryptDeriveKeyExample()
' set initial values
Dim password As String = "ThisIsMyPassword"
Dim salt As String = "ThisIsMySalt"
Dim algname As String = "RC2"
Dim hashName As String = "MD5"
Dim IV() As Byte = New Byte(7) {}
Dim keySize As Integer = 128

  ' Create and PasswordDeriveBytes
Dim pdb As PasswordDeriveBytes = _
New PasswordDeriveBytes(password, _
ASCIIEncoding.ASCII.GetBytes(salt))

  ' Extract Key
Dim key As Byte() = pdb.CryptDeriveKey(algname, hashName, keySize, IV)

  Response.Write("derived key: " + Convert.ToBase64String(key) + "<br>")
End Function






Figure 4.13: Using CryptDeriveKey: VB.NET


Figure 4.14: Example of Key Derived from a Password





Warning

Note that although you can use CryptDeriveKey to turn a short password into a strong key, keep in mind that the key is only as strong as the password itself. By using CryptDeriveKey, you are effectively reducing the key strength to that of the password, not the other way around. However, using a large number of iterations definitely will slow a brute-force attack because the attacker would have to perform those iterations for each password attempt. For a password-cracking tool, every millisecond makes a huge difference.


When using CBC or CFB modes, you must set an initialization vector. The IV works like a salt to further transform the data so that two plaintext messages encrypted with unique IVs will produce unique ciphertext data. This makes it more difficult to perform a dictionary attack on the ciphertext. Generally you want to use a random number for the IV, which SymmetricAlgorithm automatically generates when the class is created. You must read this property and store the IV so that you can later decrypt the ciphertext. If you create your own IV, you need to create one that is the same length as the key. Note that the IV is not a secret and you do not need to take special measures to protect it. Figures 4.15 and 4.16 show examples of how you can append the IV and the ciphertext so that you can store them together. They also show how you can extract the IV and decrypt the ciphertext.







      private void savingIVWithCipherText() 
{
string plainText = "This is a secret for savingIVWithCipherText";
Response.Write("plainText: " + plainText + "<br>");
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(plainText);
RijndaelManaged rijndael =
new RijndaelManaged();
rijndael.GenerateKey();
rijndael.GenerateIV();

  // Perform encryption
byte[] encryptedBytes = rijndael.CreateEncryptor().TransformFinalBlock(
buffer, 0, buffer.Length);

  // append IV to beginning of encrypted string
byte[] encryptedByteArrayWithIV =
new byte[encryptedBytes.Length + rijndael.IV.Length];
for (int x=0; x < rijndael.IV.Length; x++)
encryptedByteArrayWithIV[x] = rijndael.IV[x];
for (int x=0; x < encryptedBytes.Length; x++)
encryptedByteArrayWithIV[rijndael.IV.Length + x] =
encryptedBytes[x];

  // Display ciphertext with IV appended
string encryptedStringWithIV =
Convert.ToBase64String(encryptedByteArrayWithIV);
Response.Write("Encrypted string with IV: " +
encryptedStringWithIV + "<br>");

  // remove the IV from the beginning of the encrypted string
byte[] decryptedBytesWithIV =
Convert.FromBase64String(encryptedStringWithIV);
byte[] decryptedIV = new byte[rijndael.IV.Length];
byte[] decryptedBytes =
new byte[decryptedBytesWithIV.Length - rijndael.IV.Length];
for (int x=0; x < decryptedIV.Length; x++)
decryptedIV[x] = decryptedBytesWithIV[x];
for (int x=0; x < decryptedBytes.Length; x++)
decryptedBytes[x] =
decryptedBytesWithIV[decryptedIV.Length + x];

  // decrypted the ciphertext using the retrieved IV
rijndael.IV = decryptedIV;
string decryptedString = ASCIIEncoding.ASCII.GetString(
rijndael.CreateDecryptor().TransformFinalBlock(
decryptedBytes, 0, decryptedBytes.Length));
Response.Write("Decrypted string: " + decryptedString + "<br>");
}






Figure 4.15: Saving the IV with the Ciphertext: C#








Private Function savingIVWithCipherText()
Dim plainText As String = "This is a secret for savingIVWithCipherText"
Response.Write("plainText: " + plainText + "<br>")

  Dim buffer() As Byte = ASCIIEncoding.ASCII.GetBytes(plainText)
Dim rijndael As RijndaelManaged = New RijndaelManaged
rijndael.GenerateKey()
rijndael.GenerateIV()

  ' Perform encryption
Dim encryptedBytes As Byte() = _
rijndael.CreateEncryptor().TransformFinalBlock( _
buffer, 0, buffer.Length)

  ' append IV to beginning of encrypted string
Dim encryptedByteArrayWithIV() As Byte = _
New Byte((encryptedBytes.Length - 1) + (rijndael.IV.Length - 1)) {}

  Dim x As Integer
For x = 0 To rijndael.IV.Length - 1
encryptedByteArrayWithIV(x) = rijndael.IV(x)
Next
For x = 0 To encryptedBytes.Length - 1
encryptedByteArrayWithIV((rijndael.IV.Length - 1) + x) = _
encryptedBytes(x)

  Next

  ' Display ciphertext with IV appended
Dim encryptedStringWithIV As String = _
Convert.ToBase64String(encryptedByteArrayWithIV)
Response.Write("Encrypted string with IV: " + _
encryptedStringWithIV + "<br>")
' remove the IV from the beginning of the encrypted string
Dim decryptedBytesWithIV As Byte() = _
Convert.FromBase64String(encryptedStringWithIV)
Dim decryptedIV As Byte() = New Byte(rijndael.IV.Length - 1) {}
Dim decryptedBytes As Byte() = _
New Byte((decryptedBytesWithIV.Length - 1) - _
(rijndael.IV.Length - 1)) {}
For x = 0 To decryptedIV.Length - 1
decryptedIV(x) = decryptedBytesWithIV(x)

  Next

  For x = 0 To decryptedBytes.Length - 1
decryptedBytes(x) = decryptedBytesWithIV((decryptedIV.Length - 1) + _
x)
Next

  ' decrypted the ciphertext using the retrieved IV
rijndael.IV = decryptedIV
Dim decryptedString As String = ASCIIEncoding.ASCII.GetString( _
rijndael.CreateDecryptor().TransformFinalBlock( _
decryptedBytes, 0, decryptedBytes.Length))
Response.Write("Decrypted string: " + decryptedString + "<br>")
End Function






Figure 4.16: Saving the IV with the Ciphertext: VB.NET


Symmetric cryptography does have its limitations and weaknesses, but it also plays an important role in protecting data. The .NET Framework provides good support for well-established symmetric ciphers, and you should always encrypt sensitive data. Establish a solid framework for encryption early in your application design.

Security Policies




Use strong symmetric ciphers to ensure the privacy of data.



Never rely on XOR, ROT-13, base-64 encoding, or any homegrown encryption or obfuscation algorithm.



Avoid using DES unless absolutely necessary for backward compatibility; consider 3DES as a compatible alternative.



Use Rijndael/AES encryption for the best security and performance.



If using RC2 encryption, use 128-bit keys whenever possible.



When security is a high priority and performance a low priority, consider layering encryption algorithms.



When creating a key and IV, do not derive one from the other.



Use CryptDeriveKey to create an encryption key from a user password.




Using Asymmetric Cryptography












Summary:


Asymmetric cryptography works well for public communications but is much slower than symmetric cryptography


Threats:


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


Asymmetric cryptography addresses symmetric cryptography’s key exchange and scalability issues by using a public and private key model. Symmetric cryptography uses only one key for all data encryption and decryption, but asymmetric cryptography uses two separate keys—one for encryption and the other for decryption. The idea is that you can freely distribute your public key, which others can use to encrypt data that only you can access with your private key.

The two keys are mathematically derived from a master key, which is then destroyed. It is mathematically infeasible to create one key from the other without this master; therefore, you can distribute your public key without compromising your private key. This solution addresses the problem of key distribution because you can freely share your public key.





Tip

Although asymmetric cryptography makes it possible to share public keys, it is vital that you protect the private key. For this reason, it is generally not a safe practice to store the private key within your Web application.


One of the problems with symmetric cryptography is that you need a different key for every person with whom you communicate, and they need a different key for everyone with whom they communicate. If you have two people exchanging encrypted messages, you need just one key. If three people want to communicate securely, they need three keys; four people need six keys; and five people need 10 keys. As you can see, the number of keys required rapidly increases, severely limiting the scalability of symmetric cryptography. Asymmetric cryptography deals with this problem by using a single public key for each user; everyone can use that key to communicate with that user while maintaining a good level of integrity.

Asymmetric cryptography is processor intensive and therefore rarely used to encrypt the entire communication. Instead, most applications use asymmetric cryptography to exchange a session key and then use a symmetric cipher to encrypt the traffic during that session.

Because asymmetric cryptography often involves code on both the client and the server, you will rarely find uses for custom implementations on most public Web applications. Nevertheless, you will find yourself using asymmetric cryptography in conjunction with other technologies such as SSL. The SSL protocol uses asymmetric cryptography to establish a symmetric session key for secure data exchange. SSL maintains the security and integrity of the Web communications and authenticates the server to the client (and optionally the client to the server). You will learn more about SSL later in this chapter.


Working with Hashing Algorithms












Summary:


Hashing algorithms are one-way functions used to verify integrity of data


Threats:


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


Even though encryption is important for protecting data, sometimes it is important to be able to prove that no one has modified the data. This you can do with hashing algorithms. A hash is a one-way function that transforms data in such a way that, given a hash result (sometimes called a digest), it is computationally infeasible to produce the original message. Besides being one-way, hash functions have some other basic properties:



They take an input of any length and produce an output of a fixed length.



They should be efficient and fast to compute.



They should be computationally infeasible to invert.



They should be strongly collision free.



A hash function takes input of any length and produces a fixed-length string. That means that you can use hashes on something as small as a password or as large as an entire document. The hashing algorithms the .NET Framework provides are very efficient and fast, making them useful for many applications. The most important property of hash functions is the size of the hash. A larger hash makes it more difficult to invert the function, and it ensures that the function is collision free.

Because hash functions have a fixed output but unlimited inputs, multiple values can produce the same hash. However, because there are so many possible hash values, it is extremely difficult to find two inputs that do produce hashes that match. For that reason, hashes are like a fingerprint for the original data. If the data changes, the fingerprint will no longer match, and it is unlikely that any other useful data will produce the same fingerprint. Therefore, you can store these small fingerprints, or hashes, to later verify your data’s integrity.

Another common use for a hash is for someone to demonstrate knowledge of a piece of information without actually disclosing that information. For example, to prove you know a password, you could send the actual password, or you could produce and send the hash of that password. This is useful for Web site authentication, because the server does not have to store the actual password—it needs only the hash.

The .NET Framework supports the hashing algorithms shown in Table 4.3.



























Table 4.3: Hashing Algorithms Available in the .NET Framework

Name


Class


Hash Length


MD5


MD5CryptoServiceProvider


128 bits


SHA-1


SHA1CryptoServiceProvider SHA1Managed


160 bits


SHA-256


SHA256Managed


256 bits


SHA-384


SHA384Managed


384 bits


SHA-512


SHA512Managed


512 bits


The MD5 algorithm, defined in RFC 1321, is probably the most well-known and widely used hash function. It is the fastest of all the .NET hashing algorithms, but it uses a smaller 128-bit hash value, making it the most vulnerable to attack over the long term. MD5 has been shown to have some partial collisions and is not likely to be able to withstand future attacks as hardware capabilities increase. Nevertheless, for now it the most commonly used hashing algorithm.

SHA is an algorithm designed by the National Security Agency (NSA) and published by NIST as FIPS PUB 180. Designed for use with the Digital Signature Standard (DSS), SHA produces a 160-bit hash value.

The original SHA specification published in 1993 was shortly withdrawn by the NSA and superceded by the revised FIPS PUB 180-1, commonly referred to as SHA-1. The NSA’s reason for withdrawing the original specification was to correct a flaw in the original algorithm that reduced its cryptographic security. However, the NSA never gave details of this flaw, prompting researchers to closely examine both algorithms. Because of this close scrutiny, SHA-1 is widely considered to be quite secure.

The NIST has since published three variants of SHA-1 that produce larger hashes: SHA-256, SHA-384, and SHA-512. Although with the larger hash sizes these algorithms should be more secure, they have not undergone as much analysis as SHA-1. Nevertheless, the hash length is important to protect from brute-force and birthday attacks.

Verifying Integrity


You can use hashes to verify integrity, but many developers use them incorrectly, undoing their effectiveness. For example, many Web sites allow you to download a file as well as the MD5 checksum for that file. They do this so that you can verify the integrity of the file, but you are downloading the checksum from the same location and over the same connection as the file itself. If you don’t trust the file enough to actually need to verify the hash, how can you trust the hash that came from the same location? If someone is able to modify the file, they could just as easily compute and save a new hash.








Hacking the Code…About Birthday Attacks


Birthday attacks are based on a unique problem with hashing algorithms based on a concept called the Birthday Paradox. This puzzle is based on the fact that in a room of 183 people, there would be a 50 percent chance of one of them sharing your birthday. However, if you wanted a 50 percent chance of finding any two people who had matching birthdays, you would surprisingly only need 23 people in the room. For hashing functions, this means that it is much easier to find any two matches if you don’t care which two they are. It is possible to precompute hashes for a given password length to determine if any collisions occur.















Tip

To verify the integrity of file downloads, many Web sites provide an MD5 sum as well as a PGP signature of the sum. The MD5 sum verifies integrity, and the PGP signature proves that the MD5 sum is authentic.


Hashes are useful if you keep them private to verify data such as a cookie. For example, suppose you write a cookie to the client’s browser and store the hash of that cookie in your database. When the client returns that cookie at a later time, you can compute the hash and compare that to the one stored in the database to verify that it has not changed. Since ASP.NET stores session and authentication tokens entirely in the cookie and not on the server, it computes a hash of the cookie data and encrypts both the data and the hash. This encrypted result is encoded and saved in a cookie on the client side. When the client returns the cookie data, the server decrypts the string and verifies the hash. In this way, ASP.NET protects the hash and protects the privacy of the data.

Another way to make hashes more secure is to use a keyed hash algorithm. Keyed hashes are similar to regular hashes except that the hash is based on a secret key. To verify the hash or to create a fake hash, you need to know that key. The .NET Framework provides two keyed hashing algorithms:



HMACSHA1 This function produces a hash-based message authentication code based on the SHA-1 hashing algorithm. HMACSHA1 combines the original message and the secret key and uses SHA-1 to create a hash. It then combines that hash again with the secret key and creates a second SHA-1 hash. Like SHA-1, the HMACSHA1 algorithm produces a 160-bit hash.



MACTripleDES This algorithm uses TripleDES to encrypt the message, discarding all but the final 64 bits of the ciphertext.



With keyed hashing algorithms, you can send the hash with the data, but you must keep the key secret. Note that this method does have limitations similar to the key exchange issues of symmetric cryptography. Figures 4.17 and 4.18 demonstrate using the HMACSHA1 function.







private void HMACSHA1Example() 
{
// Setup
string plainText = "This is a secret for HMACSHA1Example";
Response.Write("plainText: " + plainText + "<br>");
byte[] key = ASCIIEncoding.ASCII.GetBytes("SecretKey");
byte[] data = ASCIIEncoding.ASCII.GetBytes(plainText);

  // Perform Hash
HMACSHA1 hmac = new HMACSHA1(key);
byte[] result = hmac.ComputeHash(data);
Response.Write("HMACSHA1Example results: " +
Convert.ToBase64String(result) + "<br>");
}






Figure 4.17: Keyed Hashing Using HMACSHA1: C#







Private Sub HMACSHA1Example()
' Setup
Dim plainText As String = "This is a secret for HMACSHA1Example"
Response.Write("plainText: " + plainText + "<br>")
Dim key() As Byte = ASCIIEncoding.ASCII.GetBytes("SecretKey")
Dim data() As Byte = ASCIIEncoding.ASCII.GetBytes(plainText)

  ' Perform Hash
Dim hmac As HMACSHA1 = New HMACSHA1(key)
Dim result() As Byte = hmac.ComputeHash(data)
Response.Write("HMACSHA1Example results: " + _
Convert.ToBase64String(result) + "<br>")
End Sub






Figure 4.18: Keyed Hashing Using HMACSHA1: VB.NET


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.







private void hashExample() 
{
string plainText = "This is a secret for hashExample";
Response.Write("plainText: " + plainText + "<br>");

  // 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) + "<br>");
}






Figure 4.19: Hashing with a Salt: C#








Private Function hashExample()
Dim plainText As String = "This is a secret for hashExample"
Response.Write("plainText: " + plainText + "<br>")

  ' 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) + "<br>")
End Function






Figure 4.20: Hashing with a Salt: VB.NET

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.



/ 96