RCA.HASH.SALT.EMPTY
空のソルトを持つ一方向性の暗号化ハッシュの使用
ソフトウェアが通信チャネルを使った送信時にパスワードなどの機密データを扱い、これら機密データの保護を一方向性の暗号化ハッシュ演算またはハッシュベースの派生キーの演算によって行う場合、ソフトウェアは入力の一部にソルトを使う暗号化関数を使用することができます。ソルトの使用は、ブルートフォース攻撃の防止に役立ちます。こうした関数を空のソルト値と共に使用した場合、暗号化の強度が下がるため、ブルートフォース攻撃を受けやすくなってしまいます。
脆弱性とリスク
ソルトが欠如していると、攻撃者がレインボーテーブルなどの辞書攻撃方法でハッシュ値を事前計算することが容易になります。ただし留意すべきなのは、ソルトを使用しても、攻撃者側の演算要件が僅かに増加するだけということです。ハッシュに良質のソルトを使用したからといって、個人のパスワードを狙う攻撃者や、ハッシュ演算に大量のリソースを利用する攻撃者に多大な負荷を課すまでには至りません。ハッシュ関数に多くの計算が伴わない場合、パスワードのオフライン攻撃も依然として実効力があります。多くの暗号化関数は効力を発揮するように設計されていますが、大量の演算リソースを用いる攻撃者に対しては、たとえハッシュによる暗号化を強化しても脆弱性をはらんでいます。
軽減と防止
適応型ハッシュ関数の使用を検討してください。適応型ハッシュ関数は、反復処理の回数や必要なメモリ量など、ハッシュ演算に要する計算負荷を変更するための設定を行うことができます。また、一部のハッシュ関数はソルト処理を自動的に行います。こうした関数は、ブルートフォース攻撃側の処理時間や負荷を大幅に増加させることができます。計算の負荷を追加する手法が実装できない場合は、予測不可なシードを取る強力な乱数ジェネレータを用いて、処理すべきパスワードごとに新しいランダムソルトを生成します。
脆弱コード例 1
この例では、Klocwork は、EVP_BytesToKey 関数が空のソルト引数で呼び出されることを示す 19 行目に 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 *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;
}
修正コード例 1
この例では、EVP_BytesToKey 関数に渡されたソルトの値は、プログラムの 2 番目のコマンドライン引数または空ではないストリング値から計算されたものです。Klocwork は 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;
}
脆弱コード例 2
この例では Klocwork は、16 行目 で PKCS5_PBKDF2_HMAC 関数がゼロの salt_length 引数で呼び出されたことを示す 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 *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;
}
修正コード例 2
この例では、ハッシュ生成のソルト長が 0 以外の引数値で PKCS5_PBKDF2_HMAC 関数が呼び出されているため、Klocwork は 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;
}
関連チェッカー
外部参考資料
セキュリティトレーニング
Secure Code Warrior が提供しているアプリケーションセキュリティトレーニング教材。
拡張機能
このチェッカーは、Klocwork knowledge base (ナレッジベース) を利用して拡張できます。詳細については、C/C++ 解析のチューニングを参照してください。