CERT C: Rec. MSC18-C

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

Описание

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

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

Реализация Polyspace

Это средство проверки проверяет на эти проблемы:

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

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

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

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

  • Чувствительная память кучи, не очищенная перед релизом.

  • Неочищенные уязвимые данные в стеке.

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

Примеры

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

Проблема

Постоянный вектор инициализации блочного шифра происходит, когда вы используете константу для вектора инициализации (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". УНИВЕРСИТЕТ КАРНЕГИ-МЕЛЛОН НЕ ДАЕТ ГАРАНТИЙ НИКАКОГО ВИДА, ИЛИ ОПИСАЛ ИЛИ ПОДРАЗУМЕВАЛ, ОТНОСИТЕЛЬНО ЛЮБОГО ВОПРОСА ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИЛ, ГАРАНТИЯ ПРИГОДНОСТИ ДЛЯ ЦЕЛИ ИЛИ ВЫСОКОГО СПРОСА, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ ЗАКАНЧИВАЕТСЯ ПОЛУЧЕННЫЙ ИЗ ИСПОЛЬЗОВАНИЯ МАТЕРИАЛА. УНИВЕРСИТЕТ КАРНЕГИ-МЕЛЛОН НЕ ДАЕТ ГАРАНТИИ НИКАКОГО ВИДА ОТНОСИТЕЛЬНО СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКОГО ПРАВА.

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