RCA.HASH.SALT.EMPTY
Use of a one-way cryptographic hash with empty salt
If software uses sensitive data such as passwords and protects them by computing a one-way cryptographic hash or a hash-based derived key from them, for example, for sending through a communication channel, the software can use cryptographic functions that use a salt as part of the input. The use of a salt helps to impede brute-force attacks. The use of such functions with an empty salt value reduces cryptographic strength and makes it easier to conduct brute force attacks.
Vulnerability and risk
The lack of a salt makes it easier for attackers to pre-compute the hash value using dictionary attack techniques such as rainbow tables. Note that the use of a salt only slightly increases the computing requirements for an attacker. The use of a good salt with a hash does not sufficiently increase the effort for an attacker who is targeting an individual password, or who has a large amount of computing resources available. Offline password cracking can still be effective if the hash function is not expensive to compute. Many cryptographic functions are designed to be efficient and can be vulnerable to attacks using massive computing resources, even if the hash is cryptographically strong.
Mitigation and prevention
Consider the use of an adaptive hash function that can be configured to change the amount of computational effort needed to compute the hash, such as the number of iterations or the amount of memory required. Some hash functions perform salting automatically. These functions can significantly increase the overhead for a brute force attack. If a technique that requires extra computational effort can not be implemented, then for each password that is processed, generate a new random salt using a strong random number generator with unpredictable seeds.
Vulnerable code example 1
In this example, Klocwork produces an RCA.HASH.SALT.EMPTY report at line 19 indicating that the function EVP_BytesToKey was invoked with an empty salt argument.
#include <openssl/evp.h>
int main( int argc, char * argv[])
{
const EVP_CIPHER *cipher;
const EVP_MD *digest = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
const unsigned char *salt = "" ;
int i;
OpenSSL_add_all_algorithms();
cipher = EVP_get_cipherbyname( "aes-256-cbc" );
if (!cipher) return -1;
digest = EVP_get_digestbyname( "md5" );
if (!digest) return -1;
if (!EVP_BytesToKey(cipher, digest, salt ,
(unsigned char *) argv[1], strlen (argv[1]),
1, key, iv)) return -3;
printf ( "Key: " ); for (i = 0; i < cipher->key_len; ++i)printf ( "%02x" , key[i]); printf ( "\n" );
printf ( "IV: " ); for (i = 0; i < cipher->iv_len; ++i) printf ( "%02x" , iv[i]); printf ( "\n" );
return 0;
}
Fixed code example 1
In this example, the salt value passed to the EVP_BytesToKey function comes from either the second command-line argument of the program, or from a non-empty string value. Klocwork does not report RCA.HASH.SALT.EMPTY.
#include <openssl/evp.h>
int main(int argc, char* argv[])
{
const EVP_CIPHER *cipher;
const EVP_MD *digest = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
const unsigned char *default_salt = "7h%ms(81klm3&wer#2";
const unsigned char *salt;
int i;
OpenSSL_add_all_algorithms();
cipher = EVP_get_cipherbyname("aes-256-cbc");
if (!cipher) return -1;
digest = EVP_get_digestbyname("md5");
if (!digest) return -1;
if (argc > 2)
salt = argv[2];
else
salt = default_salt;
if (!EVP_BytesToKey(cipher, digest, salt ,
(unsigned char *) argv[1], strlen(argv[1]),
1, key, iv)) return -3;
printf("Key: "); for(i = 0; i < cipher->key_len; ++i) printf("%02x", key[i]); printf("\n");
printf("IV: "); for(i = 0; i < cipher->iv_len; ++i) printf("%02x", iv[i]); printf("\n");
return 0;
}
Vulnerable code example 2
In this example, Klocwork produces a RCA.HASH.SALT.EMPTY report at line 16, indicating that the function PKCS5_PBKDF2_HMAC was invoked with a zero salt_length argument. This means that there is no salt provided to the function.
#include <openssl/pkcs12.h>
int main(int argc, char* argv[])
{
const EVP_MD *digest = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH];
const unsigned char *salt;
int i;
OpenSSL_add_all_algorithms();
digest=EVP_get_digestbyname("md5");
if (!digest) return -1;
if (!PKCS5_PBKDF2_HMAC(argv[1], strlen(argv[1]),
NULL, 0, -1,
digest, EVP_MAX_KEY_LENGTH, key))
return -2;
printf("Key: "); for(i = 0; i < EVP_MAX_KEY_LENGTH; ++i) printf("%02x", key[i]); printf("\n");
return 0;
}
Fixed code example 2
In this example, the function PKCS5_PBKDF2_HMAC is invoked with a non-zero salt length for hash generation, therefore, Klocwork will not report RCA.HASH.SALT.EMPTY.
#include <openssl/pkcs12.h>
int main(int argc, char* argv[])
{
const EVP_MD *digest = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH];
const unsigned char *default_salt = "7h%ms(81klm3&wer#2";
const unsigned char *salt;
int i;
OpenSSL_add_all_algorithms();
digest=EVP_get_digestbyname("md5");
if (!digest) return -1;
if (argc > 2)
salt = argv[2];
else
salt = default_salt;
if (!PKCS5_PBKDF2_HMAC(argv[1], strlen(argv[1]),
salt, strlen(salt), -1,
digest, EVP_MAX_KEY_LENGTH, key))
return -2;
printf("Key: "); for(i = 0; i < EVP_MAX_KEY_LENGTH; ++i) printf("%02x", key[i]); printf("\n");
return 0;
}
Related checkers
External guidance
Security training
Application security training materials provided by Secure Code Warrior.
Extension
This checker can be extended through the Klocwork knowledge base. See Tuning C/C++ analysis for more information.