Может кто-нибудь, пожалуйста, скажите мне, как найти недостатки безопасности в данном коде. Например: в данной программе сокетов. Любые хорошие примеры или хорошие книжные рекомендации приветствуются.
Спасибо и С уважением,
Мышь
Самым слабым плодом в этой категории будет простой поиск в источнике функций, которые часто используются неправильно или которые сложно безопасно использовать, например:
затем начните искать те, которые не так уж плохи по своей природе, но могут быть использованы неправильно. В частности, все, что записывает в буфер, может быть потенциально опасным при неправильном использовании.
ПРИМЕЧАНИЕ: все это (кроме gets
) можно использовать правильно, поэтому не думайте, что это недостаток только потому, что функция используется, вместо этого посмотрите, как она используется. Также обратите внимание, что получает
- это всегда недостаток.
ПРИМЕЧАНИЕ2: этот список не исчерпывающий, проведите небольшое исследование о часто используемых неправильно функциях и о том, как их можно избежать.
Что касается инструментов, я рекомендую такие вещи, как valgrind и splint
Вот рекомендация из книги: Написание кода безопасности . Демонстрирует не только то, как писать безопасный код, но также общие подводные камни и методы, которые обнаруживают дыры в безопасности. Он немного устарел (в моей копии написано, что он был опубликован в 2002 году), но концепции безопасности, которым он учит, все еще применимы даже 8 лет спустя.
Некоторые представители OpenBSD недавно опубликовали презентацию о своих методах кодирования .
Some source code constructs you can keep an eye out for are:
Также, защищены ли данные? Ну, если вам все равно, то это прекрасно. :-)
Некоторые инструменты, которые вы можете рассмотреть:
Одна из основных тем, которая не была затронута в ответе Эвана, - это целочисленные переполнения. Вот несколько примеров:
wchar_t *towcs(const char *s)
{
size_t l = strlen(s)+1;
mbstate_t mbs = {0};
wchar_t *w = malloc(l*sizeof *w), *w2;
if (!w || (l=mbsrtowcs(w, (char **)&s, l, &st))==-1) {
free(w);
return 0;
}
return (w2=realloc(w, l*sizeof *w)) ? w2 : w;
}
Здесь гигантская строка (> 1 гигабайта на 32-битной) произведет умножение на размер (я предполагаю, что 4) переполнение, что приведет к крошечному выделению и последующим операциям записи после его конца.
Другой более распространенный пример:
uint32_t cnt;
fread(&cnt, 1, 4, f);
cnt=ntohl(cnt);
struct record *buf = malloc(cnt * sizeof *buf);
Этот вид кода довольно часто появляется при чтении файловых / сетевых данных и подвержен таким же переполнениям.
По сути, любые арифметические операции, выполняемые со значениями, полученными из ненадежного источника, которые в конечном итоге будут использоваться в качестве размера выделения / смещения массива, должны быть проверены. Вы можете сделать это дешевым способом (наложить произвольные ограничения на считываемое значение, чтобы оно значительно выходило за пределы диапазона, который может переполниться, либо вы можете проверять переполнение на каждом шаге: Вместо:
foo = malloc((x+1)*sizeof *foo);
Вам необходимо сделать:
if (x<=SIZE_MAX-1 && x+1<=SIZE_MAX/sizeof *foo) foo = malloc((x+1)*sizeof *foo);
else goto error;
Простая команда grep для malloc / realloc с арифметическими операторами в аргументе обнаружит много таких ошибок (но не тех, где переполнение уже произошло несколькими строками выше и т. Д.).
Я посещал занятия по безопасности, где мы использовали коммерческий продукт под названием Fortify 360, который делал статический анализ кода C++. Мы проверили его на старой-старой версии OpenSSL, и он нашел кучу всего, а также предоставил руководство по устранению недостатков (которые, кстати, в последней версии OpenSSL были устранены).
В любом случае, это полезный коммерческий инструмент.