CERT C: Rec. MSC18-C

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

Описание

Управляйте определением

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

Примеры

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

Описание

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

Риск

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

Блочные шифры повреждают ваши данные в блоки фиксированного размера. Режимы блочного шифра, такие как CBC (Сцепление блоков шифра) защищают от атак с подбором по словарю XOR-лугом каждый блок с зашифрованным выводом от предыдущего блока. Чтобы защитить первый блок, эти режимы используют случайный вектор инициализации (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 (Сцепление блоков шифра) защищают от атак с подбором по словарю XOR-лугом каждый блок с зашифрованным выводом от предыдущего блока. Чтобы защитить первый блок, эти режимы используют случайный вектор инициализации (IV). Если вы используете слабый генератор случайных чисел для своего IV, ваши данные становятся уязвимыми для атак с подбором по словарю.

Фиксация

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

  • OS-level PRNG, такой как /dev/random на UNIX® или CryptGenRandom() на Windows®

  • Уровень приложения PRNG, такой как Усовершенствованный стандарт шифрования (AES) в режиме Counter (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) для ключа. Например:

  • Используйте OS-level PRNG, такой как /dev/random на UNIX или CryptGenRandom() на Windows

  • Используйте уровень приложения PRNG, такой как Усовершенствованный стандарт шифрования (AES) в режиме Counter (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); 
}

Описание

Чувствительная память кучи, не очищенная перед релизом, обнаруживает динамически выделенную память, содержащую уязвимые данные. Если вы не очищаете уязвимые данные, когда вы освобождаете память, Средство поиска Ошибки повышает дефект на функции 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); 
    }
}

Описание

Неочищенные уязвимые данные в стеке обнаруживают статическое ЗУ, содержащее уязвимые данные. Если вы не очищаете уязвимые данные от своего стека прежде, чем выйти из функции или программы, Средство поиска Ошибки повышает дефект на последней изогнутой фигурной скобке.

Риск

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

Фиксация

Прежде, чем выйти из функции или программы, уберите зоны памяти, которые содержат уязвимые данные при помощи 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 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

Это программное обеспечение и сопоставленная документация не были рассмотрены, ни являются подтвержденным Университетом Карнеги-Меллон или его Институтом программной инженерии.