メインコンテンツ

CWE Rule 311

Missing Encryption of Sensitive Data

R2023b 以降

説明

ルールの説明

The software does not encrypt sensitive or critical information before storage or transmission.

Polyspace 実装

ルール チェッカーは以下の問題をチェックします。

  • 暗号アルゴリズムがありません

  • 処理する暗号データがありません

  • 暗号化の最終ステップがありません

  • 暗号キーがありません

すべて展開する

問題

この問題は、暗号コンテキストの設定時に暗号アルゴリズムを割り当てなかった場合に発生します。

暗号コンテキストは、アルゴリズムを指定しないで初期化することができます。ただし、データを暗号化または復号化する前には、暗号コンテキストに暗号アルゴリズムを関連付けなければなりません。

リスク

暗号アルゴリズムがないと、ランタイム エラーが発生するか、少なくとも安全ではない暗号文になる可能性があります。

暗号化または復号化の前には、暗号アルゴリズムとモード、暗号化または復号化のキー、初期化ベクトル (初期化ベクトルが必要なモードの場合) など、暗号化に必要な情報をもつ暗号コンテキストを設定します。

ret = EVP_EncryptInit(&ctx, EVP_aes_128_cbc(), key, iv)
関数 EVP_aes_128_cbc() は、暗号化に高度暗号化標準 (AES) アルゴリズムを使用しなければならないことを指定します。また、128 ビットのブロック サイズと、Cipher Block Chaining (CBC) モードも指定しています。

アルゴリズムを指定する代わりに、初期化ステップで NULL を使用することができます。ただし、暗号化または復号化に暗号コンテキストを使用する前に、アルゴリズムを暗号コンテキストに関連付ける追加の初期化を実行しなければなりません。そうでない場合、暗号化または復号化の更新ステップでランタイム エラーが発生する可能性があります。

修正方法

暗号化または復号化のステップの前

ret = EVP_EncryptUpdate(&ctx, out_buf, &out_len, src, len)
暗号コンテキスト ctx にアルゴリズムを関連付けます。
ret = EVP_EncryptInit(ctx, EVP_aes_128_cbc(), key, iv)

例 — コンテキスト初期化中のアルゴリズムの未指定


#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char key[SIZE16];
unsigned char iv[SIZE16];
void func(void) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();       
    EVP_CIPHER_CTX_init(ctx);
    EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv); //Noncompliant
}

この例では、暗号コンテキスト ctx を初期化するときにアルゴリズムを指定していません。

データを暗号化または復号化する前に、暗号アルゴリズムを指定しなければなりません。2 回目の初期化を実行してアルゴリズムを指定する場合、この暗号コンテキストは完全に再初期化されます。そのため、EVP_EncryptInit_ex を使用する現在の初期化ステートメントは冗長になります。

修正 — 初期化中のアルゴリズムの指定

1 つの修正方法として、暗号コンテキストの初期化時にアルゴリズムを指定します。修正した次のコードでは、ルーチン EVP_aes_128_cbc が高度暗号化標準 (AES) アルゴリズムを呼び出します。このルーチンでは、暗号化に 128 ビットのブロック サイズと、Cipher Block Chaining (CBC) モードも指定しています。



#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char key[SIZE16];
unsigned char iv[SIZE16];
void func(unsigned char *src, int len, unsigned char *out_buf, int out_len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();       
    EVP_CIPHER_CTX_init(ctx);
    
    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv); //Compliant
    
    /* Update steps for encryption */
    EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);
}
問題

この問題は、ブロック暗号の暗号化または復号化の最終ステップを不適切に実行した場合に発生します。

たとえば、次のいずれかを行います。

  • 最終ステップを実行する前に、データの暗号化または復号化の更新ステップを実行していない。

    /* Initialization of cipher context */
    ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    ...
    /* Missing update step */
    ...
    /* Final step */
    ret = EVP_EncryptFinal_ex(ctx, out_buf, &out_len);
  • 初期化と更新のステップを挟まずに、連続して最終ステップを実行している。

    /* Initialization of cipher context */
    ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    ...
    /* Update step(s) */
    ret = EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);
    ...
    /* Final step */
    ret = EVP_EncryptFinal_ex(ctx, out_buf, &out_len);
    ...
    /* Missing initialization and update */
    ...
    /* Second final step */
    ret = EVP_EncryptFinal_ex(ctx, out_buf, &out_len);
  • 暗号コンテキストのクリーンアップを実行してから、最終ステップを実行している。

    /* Initialization of cipher context */
    ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    ...
    /* Update step(s) */
    ret = EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);
    ...
    /* Cleanup of cipher context */
    EVP_CIPHER_CTX_cleanup(ctx);
    ...
    /* Second final step */
    ret = EVP_EncryptFinal_ex(ctx, out_buf, &out_len);

リスク

ブロック暗号は、データを固定サイズのブロックに分割します。暗号化または復号化時は、更新ステップでブロック内のデータの暗号化または復号化が行われます。最終ステップでは、残りのデータがある場合にそのデータの暗号化または復号化が行われます。最終ステップでは、1 ブロックが埋まるように残りのデータにパディングが追加され、パディング後のデータの暗号化または復号化が行われます。

最終ステップを更新ステップの前に実行したり、処理するデータが存在しないときに最終ステップを実行する場合、未定義の動作になります。また、ランタイム エラーが発生する可能性もあります。

修正方法

次の順序で暗号化または復号化を実行します。

  • 暗号コンテキストの初期化

  • 更新ステップ

  • 最終ステップ

  • コンテキストのクリーンアップ

例 — 最終ステップの前に暗号化の更新ステップの未実行


#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char *out_buf;
int out_len;
unsigned char key[SIZE16];
unsigned char iv[SIZE16];

void func(void) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    
    /* Missing update steps for encryption */
    
    /* Final encryption step */
    EVP_EncryptFinal_ex(ctx, out_buf, &out_len); //Noncompliant          
}

この例には、暗号コンテキストを初期化後、データを暗号化する更新ステップがありません。更新ステップではデータの 1 つ以上のブロックを暗号化しなければなりません。最後の部分ブロックに残ったデータは最終ステップで暗号化します。最終ステップの前に更新ステップを実行しない場合、未定義の動作になります。

修正 — 最終ステップの前に暗号化の更新ステップを実行

最終ステップの前に暗号化の更新ステップを実行します。修正した次のコードでは、ルーチン EVP_EncryptUpdate が更新ステップを実行します。



#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char *out_buf;
int out_len;
unsigned char key[SIZE16];
unsigned char iv[SIZE16];

void func(unsigned char *src, int len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    
    /* Update steps for encryption */
    EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);   
    
    /* Final encryption step */
    EVP_EncryptFinal_ex(ctx, out_buf, &out_len); //Compliant          
}
問題

この問題は、データの暗号化または復号化の更新ステップ後に最終ステップを実行しなかった場合に発生します。

たとえば、次を行います。

/* Initialization of cipher context */
ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
...
/* Update step */
ret = EVP_EncryptUpdate(&ctx, out_buf, &out_len, src, len);
...
/* Missing final step */
...
/* Cleanup of cipher context */
EVP_CIPHER_CTX_cleanup(ctx);

リスク

ブロック暗号は、データを固定サイズのブロックに分割します。暗号化または復号化時は、更新ステップでブロック内のデータの暗号化または復号化が行われます。最終ステップでは、残りのデータがある場合にそのデータの暗号化または復号化が行われます。最終ステップでは、1 ブロックが埋まるように残りのデータにパディングが追加され、パディング後のデータの暗号化または復号化が行われます。

最終ステップを実行しないと、部分ブロック内の残りのデータが暗号化または復号化されません。不完全な出力や、予期しない出力になる可能性があります。

修正方法

暗号化または復号化の更新ステップの後に、最終ステップを実行して残りのデータを暗号化または復号化します。

/* Initialization of cipher context */
ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);
...
/* Update step(s) */
ret = EVP_EncryptUpdate(&ctx, out_buf, &out_len, src, len);
...
/* Final step */
ret = EVP_EncryptFinal_ex(&ctx, out_buf, &out_len);
...
/* Cleanup of cipher context */
EVP_CIPHER_CTX_cleanup(ctx);
例 — 最終ステップ前の暗号コンテキストのクリーンアップ


#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char *out_buf;
int out_len;
unsigned char key[SIZE16];
unsigned char iv[SIZE16];

void func(unsigned char *src, int len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    
    /* Update steps for encryption */
    EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);   
    
    /* Missing final encryption step */
    
    /* Cleanup of cipher context */
    EVP_CIPHER_CTX_cleanup(ctx); //Noncompliant
}

この例では、暗号化の最終ステップの前に暗号コンテキスト ctx をクリーンアップしています。最終ステップは、残りのデータを暗号化しなければなりません。最終ステップを実行しないと、暗号化は不完全になります。

修正 — 暗号化の最終ステップを実行

暗号化の更新ステップの後に、暗号化の最終ステップを実行して残りのデータを暗号化します。修正した次のコードでは、ルーチン EVP_EncryptFinal_ex が最終ステップを実行します。



#include <openssl/evp.h>
#include <stdlib.h>
#define SIZE16 16

unsigned char *out_buf;
int out_len;
unsigned char key[SIZE16];
unsigned char iv[SIZE16];

void func(unsigned char *src, int len) {
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    /* Initialization of cipher context */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv);
    
    /* Update steps for encryption */
    EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len);   
    
    /* Final encryption step */
    EVP_EncryptFinal_ex(ctx, out_buf, &out_len);
    
    /* Cleanup of cipher context */
    EVP_CIPHER_CTX_cleanup(ctx); //Compliant
}
問題

この問題は、NULL 暗号化キーまたは NULL 復号化キーを使用して暗号化または復号化した場合に発生します。

メモ

暗号コンテキストは NULL キーを使用して初期化できます。ただし、データを暗号化または復号化する前には、暗号コンテキストに非 NULL のキーを関連付けなければなりません。

リスク

NULL キーを使用して暗号化または復号化を行うと、ランタイム エラーが発生するか、少なくとも安全でない暗号文になる可能性があります。

修正方法

暗号化または復号化のステップの前

ret = EVP_EncryptUpdate(&ctx, out_buf, &out_len, src, len)
暗号コンテキスト ctx に非 NULL のキーを関連付けます。
ret = EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)

暗号コンテキストの初期化に非 NULL のキーを使用する場合もあります。

ret = EVP_EncryptInit_ex(&ctx, cipher_algo_1, NULL, key, iv)
ただし、後から暗号アルゴリズムを変更します。暗号アルゴリズムを変更するときに NULL キーを使用します。
ret = EVP_EncryptInit_ex(&ctx, cipher_algo_2, NULL, NULL, NULL)
2 番目のステートメントは暗号コンテキストを完全に再初期化しますが、NULL キーを使用しています。このような問題を避けるには、アルゴリズムを指定して暗号コンテキストを初期化するときは、必ずコンテキストにキーを関連付けます。

例 — 暗号化に NULL キーを使用


#include <openssl/evp.h>
#include <stdlib.h>
#define fatal_error() abort()

unsigned char *out_buf;
int out_len;

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv, unsigned char *src, int len){
    if (iv == NULL)
        fatal_error();
    
    /* Fourth argument is cipher key */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, iv); 
    
    /* Update step with NULL key */
    return EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len); //Noncompliant
}

この例では、コンテキスト ctx に関連付けている暗号キーは NULL です。このコンテキストを使用してデータを暗号化すると、ランタイム エラーが発生する可能性があります。

修正 — ランダムな暗号キーを使用

強力な乱数発生器を使用して暗号キーを作成します。修正した次のコードでは、openssl/rand.h で宣言されている関数 RAND_bytes を使用します。



#include <openssl/evp.h>
#include <openssl/rand.h>
#include <stdlib.h>
#define fatal_error() abort()
#define SIZE16 16

unsigned char *out_buf;
int out_len;

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv, unsigned char *src, int len){
    if (iv == NULL)
        fatal_error();
    unsigned char key[SIZE16];
    RAND_bytes(key, 16);
    
    /* Fourth argument is cipher key */
    EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv); 
    
    /* Update step with non-NULL cipher key */
    return EVP_EncryptUpdate(ctx, out_buf, &out_len, src, len); //Compliant
}

チェック情報

カテゴリ: その他

バージョン履歴

R2023b で導入