Будьте осторожны при обработке конфиденциальных данных, таких как пароли, в коде программы
Будьте осторожны при обработке конфиденциальных данных, таких как пароли, в коде программы.[1]
Эта проверка проверяет на наличие следующих проблем:
Вектор инициализации постоянного блочного шифра.
Постоянный ключ шифра.
Предсказуемый вектор инициализации блочного шифра.
Предсказуемый ключ шифра.
Чувствительная куча памяти не очищена перед релизом.
Неубранные конфиденциальные данные в стеке.
Небезопасная стандартная функция шифрования.
Вектор инициализации постоянного блочного шифра возникает, когда вы используете константу для вектора инициализации (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
не является входным и использует небезопасный алгоритм Data Encryption Standard.
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) |
[1] Это программное обеспечение было создано MathWorks, включающее фрагменты: «Сайт SEI CERT-C», © 2017 Университет Карнеги Меллон, Веб-сайт SEI CERT-C + + © 2017 Университет Карнеги Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.
ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОН И/ИЛИ ЕГО ИНЖЕНЕРНОГО ИНСТИТУТА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ НА БАЗИСЕ «КАК ЕСТЬ». УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБОГО ВОПРОСА, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИЮ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ КОММЕРЧЕСКОЙ ВЫГОДЫ, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.
Это программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллон или его Институтом программной инженерии.
1. Если смысл перевода понятен, то лучше оставьте как есть и не придирайтесь к словам, синонимам и тому подобному. О вкусах не спорим.
2. Не дополняйте перевод комментариями “от себя”. В исправлении не должно появляться дополнительных смыслов и комментариев, отсутствующих в оригинале. Такие правки не получится интегрировать в алгоритме автоматического перевода.
3. Сохраняйте структуру оригинального текста - например, не разбивайте одно предложение на два.
4. Не имеет смысла однотипное исправление перевода какого-то термина во всех предложениях. Исправляйте только в одном месте. Когда Вашу правку одобрят, это исправление будет алгоритмически распространено и на другие части документации.
5. По иным вопросам, например если надо исправить заблокированное для перевода слово, обратитесь к редакторам через форму технической поддержки.