メインコンテンツ

CWE Rule 547

Use of Hard-coded, Security-relevant Constants

R2023a 以降

説明

ルールの説明

The program uses hard-coded constants instead of symbolic names for security-critical values, which increases the likelihood of mistakes during code maintenance or security policy change.

Polyspace 実装

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

  • バッファー サイズがハードコードされています

  • ループ境界がハード コードされています

  • メモリの操作に使用するオブジェクトのサイズがハードコードされています

  • ハードコードされた機密データ

すべて展開する

問題

この問題は、配列などのメモリ バッファーを宣言する際に、シンボリック定数ではなく数値を使用した場合に発生します。

リスク

バッファー サイズがハード コードされていると、次の問題の原因となります。

  • バッファー サイズがハード コードされていると、誤りが生じる可能性が大きくなり、そのため保守コストも高くなる。ポリシーの変更によりバッファー サイズの変更が必要になった場合、開発者はコードにバッファー サイズが出現するたびに変更しなければならない。

  • ハード コードされている定数は、コードが開示された場合に攻撃にさらされることがある。

修正方法

バッファー サイズには、ハード コードされている定数ではなくシンボリック名を使用します。シンボリック名には、const 修飾子付き変数、enum 定数またはマクロを含めます。

enum 定数の使用を推奨します。

  • マクロは前処理後に、その定数値によって置き換えられます。したがって、ループ境界が外部にさらされる場合があります。

  • enum 定数はコンパイルの時点で既知となっています。したがって、コンパイラではループをより効率的に最適化できます。

    const 修飾子付き変数は、多くの場合、実行時に既知となります。

例 — ハードコードされたバッファー サイズ
int table[100];  //Noncompliant

void read(int);

void func(void) {
    for (int i=0; i<100; i++) //Noncompliant
        read(table[i]);
}

この例では、配列 table のサイズがハード コードされています。この例では、for ループのループ境界もハードコードされているため、2 つ目のルール違反があります。

修正 — シンボリック名を使用

考えられる 1 つの修正方法として、ハード コードされているサイズをシンボリック名に置き換えます。

const int MAX_1 = 100;        
#define MAX_2 100
enum { MAX_3 = 100 };

int table_1[MAX_1];
int table_2[MAX_2];
int table_3[MAX_3];

void read(int);

void func(void) {
    for (int i=0; i < MAX_1; i++)
        read(table_1[i]);
    for (int i=0; i < MAX_2; i++)
        read(table_2[i]);
    for (int i=0; i < MAX_3; i++)
        read(table_3[i]);
}
問題

この問題は、forwhile、または do-while ループの境界にシンボリック定数ではなく数値を使用した場合に発生します。

リスク

ループ境界がハード コードされていると、次の問題の原因となります。

  • ループ境界がハード コードされていると、時間のかかる計算やリソース割り当てがループに含まれている場合に、サービス拒否攻撃に対する脆弱性が生じる。

  • ループ境界がハード コードされていると、誤りが生じる可能性が大きくなり、保守コストが高くなる。ポリシーの変更によりループ境界の変更が必要になった場合、開発者はコードに境界が出現するたびに変更しなければならない。

    たとえば、ループ境界が 10000 であり、ネットワーク サーバー アプリケーションでサポートされているクライアント接続数の最大値を表現しているとします。サーバーでサポートしているクライアント数がこれより多い場合、コード内のループ境界のインスタンスをすべて変更しなければなりません。ループ境界が 1 回しか発生しない場合でも、コード内の 10000 の数値に対して検索しなければなりません。この数値は、ループ境界以外の場所にも現れる可能性があります。こうした場所は、ループ境界を検索する前に参照しておかなければなりません。

修正方法

ループ境界に、ハード コードされている定数ではなくシンボリック名を使用します。シンボリック名には、const 修飾子付き変数、enum 定数またはマクロが含まれます。以下の理由により、enum 定数の使用が推奨されます。

  • マクロは前処理後に、その定数値によって置き換えられます。したがって、バッファー サイズが外部にさらされる場合があります。

  • enum 定数はコンパイルの時点で既知となっています。したがって、コンパイラではストレージをより効率的に割り当てられます。

    const 修飾子付き変数は、多くの場合、実行時に既知となります。

例 — ハードコードされたループ境界
void performOperation(int);

void func(void) {
    for (int i=0; i<100; i++)  //Noncompliant
        performOperation(i);
}

この例では、for ループの境界がハード コードされています。

修正 — シンボリック名を使用

考えられる 1 つの修正方法として、ハード コードされているループ境界をシンボリック名に置き換えます。

const int MAX_1 = 100;
#define MAX_2 100
enum { MAX_3 = 100 };

void performOperation_1(int);
void performOperation_2(int);
void performOperation_3(int);

void func(void) {
    for (int i=0; i<MAX_1; i++)
        performOperation_1(i);
    for (int i=0; i<MAX_2; i++)
        performOperation_2(i);
    for (int i=0; i<MAX_3; i++)
        performOperation_3(i);
}
問題

この問題は、ハードコードされている定数を次のメモリ関数のメモリ サイズ引数として使用する場合に発生します。

  • malloccalloc などの動的メモリ割り当て関数。

  • memcpymemmovememcmpmemset などのメモリ操作関数。

メモリ サイズをハードコードしている場合、文字列リテラルを使用してメモリ操作を実行すると、Polyspace® は欠陥を報告しません。

リスク

オブジェクトのサイズをハードコードすると、コードが、型のサイズが異なるアーキテクチャに移植できません。定数値がオブジェクトのサイズと同じでない場合、バッファーがオーバーフローしたり、しなかったりする可能性があります。

修正方法

メモリ関数のサイズ引数には、sizeof(object) を使用します。

例 — 4 バイトの整数ポインターを仮定
#include <stddef.h>
#include <stdlib.h>
enum {
    SIZE3   = 3,
    SIZE20  = 20
};
extern void fill_ints(int **matrix, size_t nb, size_t s);

void bug_hardcodedmemsize()
{
    size_t i, s;

    s = 4;
    int **matrix = (int **)calloc(SIZE20, s);  //Noncompliant
    if (matrix == NULL) {
        return; /* Indicate calloc() failure */
    }
    fill_ints(matrix, SIZE20, s);
    free(matrix);
}

この例では、メモリ割り当て関数 calloc が 4 バイトのメモリ サイズで呼び出されています。このメモリは整数ポインターに対して割り当てられています。整数ポインターは、ターゲットによって 4 バイトよりも大きい場合も小さい場合もあります。整数型ポインターが 4 バイトでない場合、プログラムは失敗する可能性があります。

修正 — sizeof(int *) を使用

calloc を呼び出す際、ハード コードしているサイズを sizeof の呼び出しに置き換えます。この変更によりコードの移植性が高くなります。

#include <stddef.h>
#include <stdlib.h>
enum {
    SIZE3   = 3,
    SIZE20  = 20
};
extern void fill_ints(int **matrix, size_t nb, size_t s);

void corrected_hardcodedmemsize()
{
    size_t i, s;

    s = sizeof(int *);
    int **matrix = (int **)calloc(SIZE20, s);
    if (matrix == NULL) {
        return; /* Indicate calloc() failure */
    }
    fill_ints(matrix, SIZE20, s);
    free(matrix);
}

問題

この問題は、機密性の高いデータが文字列リテラルなどとしてコード内で直接露出された場合に発生します。チェッカーは、特定のデータを機密性が高いとして識別し、パスワード暗号化関数などの特定の関数で使用されないようにします。

以下のデータは機密性が高い可能性があります。

データの型情報の機密性を示唆する関数
ホスト名
  • sethostnamesetdomainnamegethostbynamegethostbyname2getaddrinfogethostbyname_rgethostbyname2_r (文字列引数)

  • inet_atoninet_ptoninet_net_ptoninet_addrinet_network (文字列引数)

  • mysql_real_connect mysql_real_connect_nonblockingmysql_connect (第 2 引数)

パスワード
  • CreateProcessWithLogonWLogonUser (第 1 引数)

  • mysql_real_connect mysql_real_connect_nonblockingmysql_connect (第 3 引数)

データベース
  • MySQL: mysql_real_connect mysql_real_connect_nonblockingmysql_connect (第 4 引数)

  • SQLite: sqlite3_opensqlite3_open16sqlite3_open_v2 (第 1 引数)

  • PostgreSQL: PQconnectdb

  • Microsoft SQL:SQLDriverConnect (第 3 引数)

ユーザー名
  • getpw, getpwnam, getpwnam_r, getpwuid, getpwuid_r

ソルトcryptcrypt_r (第 2 引数)
暗号化キーと初期化ベクトル

OpenSSL:

  • EVP_CipherInitEVP_EncryptInitEVP_DecryptInit (第 3 引数)

  • EVP_CipherInit_exEVP_EncryptInit_exEVP_DecryptInit_ex (第 4 引数)

シード
  • srandsrandominitstate (第 1 引数)

  • OpenSSL: RAND_seedRAND_add

リスク

ハードコードされた情報は、コードから生成されたバイナリから問い合わせることができます。

修正方法

機密情報のハードコーディングを回避します。

例 — 文字列リテラルを介して露出された機密データ
// Typically, you include the header "mysql.h" with function and type declarations.
// In this example, only the required lines from the header are quoted.

typedef struct _MYSQL MYSQL;

MYSQL *mysql_real_connect(MYSQL *mysql,
                          const char *host, const char *user, const char *passwd,
                          const char *db, unsigned int port, const char *unix_socket,
                          unsigned long client_flag);

typedef void * DbHandle;
extern MYSQL *sql;

// File that uses functions from "mysql.h" 
const char *host = "localhost";
char *user = "guest";
char *passwd;

DbHandle connect_to_database_server(const char *db)
{
    passwd = (char*)"guest";
    return (DbHandle)
        mysql_real_connect (sql, host, user, passwd, db, 0, 0x0, 0);  //Noncompliant 
}

この例では、引数の host (ホスト名)、user (ユーザー名)、および passwd (パスワード) が文字列リテラルで、コードで直接露出されます。

ASCII 文字列の生成されたバイナリを問い合わせると、この情報が公開されます。

修正 – 保護された構成ファイルから機密データを読み取る

1 つの修正方法として、構成ファイルからデータを読み取ります。次の修正例では、関数 connect_to_database_server_init を呼び出すことによって、保護された構成ファイルからホスト名、ユーザー名、およびパスワードがその引数に読み取られる可能性があります。

// Typically, you include the header "mysql.h" with function and type declarations.
// In this example, only the required lines from the header are quoted.

typedef struct _MYSQL MYSQL;

MYSQL *mysql_real_connect(MYSQL *mysql,
                          const char *host, const char *user, const char *passwd,
                          const char *db, unsigned int port, const char *unix_socket,
                          unsigned long client_flag);

typedef void * DbHandle;
extern MYSQL *sql;

// File that uses functions from "mysql.h" 

int connect_to_database_server_init(const char **host,
                                    const char **user,
                                    const char **passwd,
                                    const char **db);

DbHandle connect_to_database_server(const char *db)
{
    const char *host_from_cfg;
    const char *user_from_cfg;
    const char *passwd_from_cfg;
    const char *db_from_cfg;
    if (connect_to_database_server_init(&host_from_cfg,
                                        &user_from_cfg,
                                        &passwd_from_cfg,
                                        &db_from_cfg))
    {
        return (DbHandle)
            mysql_real_connect (sql, host_from_cfg, user_from_cfg, 
                        passwd_from_cfg, db_from_cfg,  0, 0x0, 0);
    }
    else
        return (DbHandle)0x0;
}

チェック情報

カテゴリ: Bad Coding Practices

バージョン履歴

R2023a で導入