exponenta event banner

CERT C: Rec. MSC18-C

Будьте осторожны при обработке конфиденциальных данных, таких как пароли, в программном коде

Описание

Определение правила

Будьте осторожны при обработке конфиденциальных данных, например паролей, в программном коде [1 ].

Внедрение Polyspace

Эта проверка проверяет наличие следующих проблем:

  • Вектор инициализации постоянного блочного шифра.

  • Постоянный шифровальный ключ.

  • Предсказуемый вектор инициализации блочного шифра.

  • Предсказуемый ключ шифра.

  • Конфиденциальная память кучи не очищена перед выпуском.

  • Неочищенные конфиденциальные данные в стеке.

  • Небезопасная стандартная функция шифрования.

Примеры

развернуть все

Проблема

Вектор инициализации постоянного блочного шифра возникает при использовании константы для вектора инициализации (IV) во время шифрования.

Риск

Использование постоянной IV эквивалентно неиспользованию IV. Ваши зашифрованные данные уязвимы для словарных атак.

Блочные шифры разбивают данные на блоки фиксированного размера. Блочные режимы шифрования, такие как CBC (Cipher Block Chaining), защищают от словарных атак с помощью XOR-ing каждого блока с зашифрованным выходом из предыдущего блока. Для защиты первого блока эти режимы используют случайный вектор инициализации (IV). При использовании константы IV для шифрования нескольких потоков данных, имеющих общее начало, данные становятся уязвимыми для словарных атак.

Зафиксировать

Создайте случайный IV с помощью сильного генератора случайных чисел.

Список генераторов случайных чисел, которые являются криптографически слабыми, см. в разделе Vulnerable pseudo-random number generator.

Пример - Константы, используемые для вектора инициализации

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

/* Using the cryptographic routines */

int func(EVP_CIPHER_CTX *ctx, unsigned char *key){
    unsigned char iv[SIZE16] = {'1', '2', '3', '4','5','6','b','8','9',
                                 '1','2','3','4','5','6','7'};
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}

В этом примере вектор инициализации iv имеет только константы. Вектор постоянной инициализации делает ваш шифр уязвимым для словарных атак.

Коррекция - использовать вектор случайной инициализации

Одной из возможных корректировок является использование сильного генератора случайных чисел для создания вектора инициализации. Исправленный код здесь использует функцию RAND_bytes объявлен в openssl/rand.h.


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

/* Using the cryptographic routines */

int func(EVP_CIPHER_CTX *ctx, unsigned char *key){
    unsigned char iv[SIZE16];
    RAND_bytes(iv, 16);
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}
Проблема

Постоянный ключ шифрования возникает при использовании константы для ключа шифрования или дешифрования.

Риск

Если вы используете константу для ключа шифрования или дешифрования, злоумышленник может легко извлечь ваш ключ.

Для шифрования и последующей расшифровки данных используется ключ. Если ключ легко извлекается, данные, зашифрованные с помощью этого ключа, не являются безопасными.

Зафиксировать

Создание случайного ключа с помощью мощного генератора случайных чисел.

Список генераторов случайных чисел, которые являются криптографически слабыми, см. в разделе Vulnerable pseudo-random number generator.

Пример - Константы, используемые для ключа

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

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv){
    unsigned char key[SIZE16] = {'1', '2', '3', '4','5','6','b','8','9',
                                 '1','2','3','4','5','6','7'};
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}

В этом примере ключ шифра, key, имеет только константы. Злоумышленник может легко извлечь постоянный ключ.

Коррекция - использовать случайный ключ

Используйте сильный генератор случайных чисел для создания ключа шифра. Исправленный код здесь использует функцию RAND_bytes объявлен в openssl/rand.h.


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

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv){
    unsigned char key[SIZE16];
    RAND_bytes(key, 16);
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}
Проблема

Предсказуемый вектор инициализации блочного шифра возникает, когда используется слабый генератор случайных чисел для вектора инициализации блочного шифра.

Риск

Если для вектора инициации используется слабый генератор случайных чисел, данные уязвимы для словарных атак.

Блочные шифры разбивают данные на блоки фиксированного размера. Блочные режимы шифрования, такие как CBC (Cipher Block Chaining), защищают от словарных атак с помощью XOR-ing каждого блока с зашифрованным выходом из предыдущего блока. Для защиты первого блока эти режимы используют случайный вектор инициализации (IV). Если вы используете слабый генератор случайных чисел для вашего IV, ваши данные становятся уязвимыми для словарных атак.

Зафиксировать

Используйте сильный генератор псевдослучайных чисел (PRNG) для вектора инициализации. Например, используйте:

  • PRNG уровня ОС, например, /dev/random в UNIX ® илиCryptGenRandom() в ОС Windows ®

  • PRNG на уровне приложений, например, Advanced Encryption Standard (AES) в режиме счетчика (CTR), HMAC-SHA1 и т.д.

Список генераторов случайных чисел, которые являются криптографически слабыми, см. в разделе Vulnerable pseudo-random number generator.

Пример - Предсказуемый вектор инициализации

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

int func(EVP_CIPHER_CTX *ctx, unsigned char *key){
    unsigned char iv[SIZE16];
    RAND_pseudo_bytes(iv, 16);
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}

В этом примере функция RAND_pseudo_bytes объявлен в openssl/rand.h создает вектор инициализации. Последовательности байтов, которые RAND_pseudo_bytes генерация не обязательно непредсказуема.

Коррекция - использовать генератор сильных случайных чисел

Используйте генератор сильных случайных чисел для создания вектора инициализации. Исправленный код здесь использует функцию RAND_bytes объявлен в openssl/rand.h.


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

int func(EVP_CIPHER_CTX *ctx, unsigned char *key){
    unsigned char iv[SIZE16];
    RAND_bytes(iv, 16);
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}
Проблема

Предсказуемый ключ шифрования возникает при использовании слабого генератора случайных чисел для ключа шифрования или дешифрования.

Риск

Если вы используете слабый генератор случайных чисел для ключа шифрования или дешифрования, злоумышленник может легко извлечь ваш ключ.

Для шифрования и последующей расшифровки данных используется ключ. Если ключ легко извлекается, данные, зашифрованные с помощью этого ключа, не являются безопасными.

Зафиксировать

Используйте сильный генератор псевдослучайных чисел (PRNG) для ключа. Например:

  • Использование PRNG уровня ОС, например /dev/random в UNIX или CryptGenRandom() в Windows

  • Используйте PRNG на уровне приложений, например Advanced Encryption Standard (AES) в режиме счетчика (CTR), HMAC-SHA1 и т.д.

Список генераторов случайных чисел, которые являются криптографически слабыми, см. в разделе Vulnerable pseudo-random number generator.

Пример - Предсказуемый ключ шифра

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

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv){
    unsigned char key[SIZE16];
    RAND_pseudo_bytes(key, 16);
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}

В этом примере функция RAND_pseudo_bytes объявлен в openssl/rand.h создает шифровальный ключ. Однако последовательности байтов, которые RAND_pseudo_bytes генерация не обязательно непредсказуема.

Коррекция - использовать генератор сильных случайных чисел

Одной из возможных корректировок является использование сильного генератора случайных чисел для создания шифровального ключа. Исправленный код здесь использует функцию RAND_bytes объявлен в openssl/rand.h.


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

int func(EVP_CIPHER_CTX *ctx, unsigned char *iv){
    unsigned char key[SIZE16];
    RAND_bytes(key, 16);
    return EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv, 1); 
}
Проблема

Чувствительная память кучи не очищается перед освобождением и обнаруживает динамически выделенную память, содержащую конфиденциальные данные. Если вы не очистите конфиденциальные данные, когда освободите память, Bug Finder поднимет дефект на free функция.

Риск

Если зона памяти перераспределена, злоумышленник может проверить конфиденциальные данные в старой зоне памяти.

Зафиксировать

Перед вызовом free, очистить конфиденциальные данные с помощью memset или SecureZeroMemory.

Пример - Чувствительный буфер освобожден, не очищен
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>

void sensitiveheapnotcleared(const char * my_user) {
    struct passwd* result, pwd;
    long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
    char* buf = (char*) malloc(1024);
    getpwnam_r(my_user, &pwd, buf, bufsize, &result);
    free(buf);
}

В этом примере функция использует буфер паролей и освобождает память до конца функции. Однако данные в памяти не очищаются с помощью free команда.

Исправление - обнуление данных

Одной из возможных корректировок является запись над данными, чтобы очистить конфиденциальную информацию. В этом примере используется memset для записи по данным с нулями.

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <assert.h>

#define isNull(arr) for(int i=0;i<(sizeof(arr)/sizeof(arr[0]));i++) assert(arr[i]==0)

void sensitiveheapnotcleared(const char * my_user) {
    struct passwd* result, pwd;
    long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
    char* buf = (char*) malloc(1024);

    if (buf) {
        getpwnam_r(my_user, &pwd, buf, bufsize, &result);
        memset(buf, 0, (size_t)1024);
        isNull(buf);
        free(buf); 
    }
}
Проблема

Неочищенные конфиденциальные данные в стеке обнаруживают статическую память, содержащую конфиденциальные данные. Если вы не очистите конфиденциальные данные из стека перед выходом из функции или программы, Bug Finder поднимет дефект на последней фигурной скобке.

Риск

Сохранение в стеке конфиденциальной информации, например паролей или сведений о пользователе, позволяет злоумышленнику получить дополнительный доступ к этой информации после завершения программы.

Зафиксировать

Перед выходом из функции или программы удалите зоны памяти, содержащие конфиденциальные данные, с помощью memset или SecureZeroMemory.

Пример - Статический буфер парольной информации
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>

void bug_sensitivestacknotcleared(const char * my_user) {
    struct passwd* result, pwd;
    long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
    char buf[1024] = "";
    getpwnam_r(my_user, &pwd, buf, bufsize, &result);
} 

В этом примере статический буфер заполняется паролем. Программа освобождает память стека в конце программы. Однако данные все еще доступны из памяти.

Коррекция - очистка памяти

Одной из возможных корректировок является запись в память перед выходом из функции. В этом примере используется memset для очистки данных из буферной памяти.

#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <pwd.h>
#include <assert.h>

#define isNull(arr) for(int i=0; i<(sizeof(arr)/sizeof(arr[0])); i++) assert(arr[i]==0)

void corrected_sensitivestacknotcleared(const char * my_user) {
    struct passwd* result, pwd;
    long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
    char buf[1024] = "";
    getpwnam_r(my_user, &pwd, buf, bufsize, &result);
    memset(buf, 0, (size_t)1024);
    isNull(buf);
}
Проблема

Небезопасная стандартная функция шифрования обнаруживает использование функций с нарушенным или слабым криптографическим алгоритмом. Например, crypt не является входящим и основан на рискованном стандарте шифрования данных (DES).

Риск

Использование нарушенного, слабого или нестандартного алгоритма может предоставить злоумышленнику конфиденциальную информацию. Определенный хакер может осуществлять доступ к защищенным данным с использованием различных методов.

Если слабая функция не входит, то при использовании функции в параллельных программах возникает дополнительный риск расового состояния.

Зафиксировать

Избегайте функций, использующих эти алгоритмы шифрования. Вместо этого используйте функцию повторного ввода, использующую более сильный алгоритм шифрования.

Примечание

Некоторые реализации crypt поддержка дополнительных, возможно более безопасных алгоритмов шифрования.

Пример - Расшифровка пароля с помощью crypt
#define _GNU_SOURCE
#include <pwd.h>
#include <string.h>
#include <crypt.h>

volatile int rd = 1;

const char *salt = NULL;
struct crypt_data input, output;

int verif_pwd(const char *pwd, const char *cipher_pwd, int safe)
{
    int r = 0;
    char *decrypted_pwd = NULL;
    
    switch(safe)
    {
      case 1: 
        decrypted_pwd = crypt_r(pwd, cipher_pwd, &output);
        break;
        
      case 2: 
        decrypted_pwd = crypt_r(pwd, cipher_pwd, &output);
        break;
        
      default:
        decrypted_pwd = crypt(pwd, cipher_pwd); 
        break;
    }
    
    r = (strcmp(cipher_pwd, decrypted_pwd) == 0); 
    
    return r;
}

В этом примере: crypt_r и crypt расшифровать пароль. Однако crypt не входит и использует небезопасный алгоритм шифрования данных.

Коррекция - использование crypt_r

Одной из возможных корректировок является замена crypt с crypt_r.

#define _GNU_SOURCE
#include <pwd.h>
#include <string.h>
#include <crypt.h>

volatile int rd = 1;

const char *salt = NULL;
struct crypt_data input, output;

int verif_pwd(const char *pwd, const char *cipher_pwd, int safe)
{
    int r = 0;
    char *decrypted_pwd = NULL;
    
    switch(safe)
    {
      case 1: 
        decrypted_pwd = crypt_r(pwd, cipher_pwd, &output);
        break;
        
      case 2: 
        decrypted_pwd = crypt_r(pwd, cipher_pwd, &output);
        break;
        
      default:
        decrypted_pwd = crypt_r(pwd, cipher_pwd, &output);  
        break;
    }
    
    r = (strcmp(cipher_pwd, decrypted_pwd) == 0);
    
    return r;
}

Проверить информацию

Группа: Rec. 48. Разное (MSC)
Представлен в R2019a

[1] Данное программное обеспечение было создано компанией MathWorks и включает в себя следующие компоненты: «Веб-сайт SEI CERT-C», © 2017 Университет Карнеги-Меллон, веб-сайт SEI CERT-C + + © 2017 Университет Карнеги-Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги-Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОНА И/ИЛИ ЕГО ПРОГРАММНОГО ИНЖЕНЕРНОГО ИНСТИТУТА, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ КАК ЕСТЬ. УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБЫХ ВОПРОСОВ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ ТОВАРНОЙ ПРИГОДНОСТИ, ИСКЛЮЧИТЕЛЬНОСТИ ИЛИ РЕЗУЛЬТАТОВ, ПОЛУЧЕННЫХ ОТ ИСПОЛЬЗОВАНИЯ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Данное программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллона или его Институтом разработки программного обеспечения.