Insecure Use of Cryptography
Why is this important?
Cryptography is hard. And when it is used in an application, it's usually to make sure user data is secure in transit and at rest. Unfortunately, cryptographic libraries are not always easy to use. They require proper configuration and settings to ensure the data is safe.
Check out this video for a high-level explanation:
Fixing Insecure Use of Cryptography
Option A: Use A Strong Random Number Generator
The use of a predictable random values can lead to vulnerabilities when used in certain security critical contexts.
References:
- OWASP: Insecure Randomness
- CWE-330: Use of Insufficiently Random Values
- CWE-338: Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG)
- CWE-331: Insufficient Entropy
Follow these steps:
- Go through the issues that GuardRails identified in the PR.
- Identify the following pattern:
var rnd = new Random();
byte[] buffer = new byte[16];
rnd.GetBytes(buffer);
return BitConverter.ToString(buffer);
- And replace it with:
using System.Security.Cryptography;
var rnd = RandomNumberGenerator.Create();
- Test it, ship it 🚢 and relax 🌴
Option B: Use A Strong Hashing Function
MD5
or SHA1
have known collision weaknesses and are no longer considered strong hashing algorithms.
References:
Follow these steps:
- Go through the issues that GuardRails identified in the PR.
- Identify the following pattern:
var hashProvider = new SHA1CryptoServiceProvider();
var hash = hashProvider.ComputeHash(str);
- And replace it with:
var hashProvider = SHA256Managed.Create();
var hash = hashProvider.ComputeHash(str);
- Test it, ship it 🚢 and relax 🌴
Option C: Use A Strong Cipher Algorithm
DES
and 3DES
are not considered a strong cipher for modern applications. Currently, NIST recommends the usage of AES block ciphers instead. Broken or deprecated ciphers have known weakness. Attackers may brute force the secret key that was used for the encryption.
References:
- NIST Withdraws Outdated Data Encryption Standard
- CWE-326: Inadequate Encryption Strength
- StackOverflow: Authenticated encryption example
Follow these steps:
- Go through the issues that GuardRails identified in the PR.
- Identify the following pattern:
DES DESalg = DES.Create();
// Create a string to encrypt.
byte[] encrypted;
ICryptoTransform encryptor = DESalg.CreateEncryptor(key, zeroIV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor,
CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(Data);
}
encrypted = msEncrypt.ToArray();
return encrypted;
}
}
- And replace it with AES:
// Create a string to encrypt.
byte[] encrypted;
var encryptor = new AesManaged();
encryptor.Key = key;
encryptor.GenerateIV();
var iv = encryptor.IV;
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor.CreateEncryptor(),
CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(Data);
}
encrypted = msEncrypt.ToArray();
return encrypted;
}
}
- Test it, ship it 🚢 and relax 🌴
Option D: Avoid Weak CBC/ECB Modes
If attackers are able to submit encrypted payload and the server is decrypting its content. The attacker is likely able to decrypt its content because the CBC mode is susceptible to padding oracle attacks.
The ECB mode will produce identical encrypted block for equivalent plain text block. This could allow an attacker that is eavesdropping to guess the content sent. This same property can also allow the recovery of the original message. Furthermore, this cipher mode alone does not guarantee integrity. See also this resource for more information.
References:
- Wikipedia: Authenticated encryption
- NIST: Authenticated Encryption Modes
- CAPEC: Padding Oracle Crypto Attack
- CWE-696: Incorrect Behavior Order
Follow these steps:
- Go through the issues that GuardRails identified in the PR.
- Identify the following pattern:
using (var aes = new AesManaged {
KeySize = KeyBitSize,
BlockSize = BlockBitSize,
Mode = CipherMode.OFB,
Padding = PaddingMode.PKCS7
})
{
using (var encrypter = aes.CreateEncryptor(cryptKey, new byte[16]))
using (var cipherStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cryptoStream))
{
//Encrypt Data
binaryWriter.Write(secretMessage);
}
cipherText = cipherStream.ToArray();
}
}
//Missing HMAC suffix to assure integrity
- And replace it with:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
public static readonly int BlockBitSize = 128;
public static readonly int KeyBitSize = 256;
public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] key)
{
//User Error Checks
if (key == null || key.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");
if (secretMessage == null || secretMessage.Length == 0)
throw new ArgumentException("Secret Message Required!", "secretMessage");
//Using random nonce large enough not to repeat
var nonce = new byte[NonceBitSize / 8];
Random.NextBytes(nonce, 0, nonce.Length);
var cipher = new GcmBlockCipher(new AesFastEngine());
var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, new byte[0]);
cipher.Init(true, parameters);
//Generate Cipher Text With Auth Tag
var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
cipher.DoFinal(cipherText, len);
//Assemble Message
using (var combinedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(combinedStream))
{
//Prepend Nonce
binaryWriter.Write(nonce);
//Write Cipher Text
binaryWriter.Write(cipherText);
}
return combinedStream.ToArray();
}
}
or:
using System.IO;
using System.Security.Cryptography;
public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
{
//User Error Checks
if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "cryptKey");
if (authKey == null || authKey.Length != KeyBitSize / 8)
throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "authKey");
if (secretMessage == null || secretMessage.Length < 1)
throw new ArgumentException("Secret Message Required!", "secretMessage");
byte[] cipherText;
byte[] iv;
using (var aes = new AesManaged {
KeySize = KeyBitSize,
BlockSize = BlockBitSize,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
})
{
//Use random IV
aes.GenerateIV();
iv = aes.IV;
using (var encrypter = aes.CreateEncryptor(cryptKey, iv))
using (var cipherStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
using (var binaryWriter = new BinaryWriter(cryptoStream))
{
//Encrypt Data
binaryWriter.Write(secretMessage);
}
cipherText = cipherStream.ToArray();
}
}
//Assemble encrypted message and add authentication
using (var hmac = new HMACSHA256(authKey))
using (var encryptedStream = new MemoryStream())
{
using (var binaryWriter = new BinaryWriter(encryptedStream))
{
//Prepend IV
binaryWriter.Write(iv);
//Write Ciphertext
binaryWriter.Write(cipherText);
binaryWriter.Flush();
//Authenticate all data
var tag = hmac.ComputeHash(encryptedStream.ToArray());
//Postpend tag
binaryWriter.Write(tag);
}
return encryptedStream.ToArray();
}
}
- Test it, ship it 🚢 and relax 🌴