Вопрос

«Средний человек не хочет быть свободным. Он просто хочет быть в безопасности». - HL Menken

Я пытаюсь написать очень безопасно C. Ниже я перечисляю некоторые методы, которые я использую, и спрашиваю, они столь же безопасны, как я думаю. Пожалуйста, не стесняйтесь разорвать мой код/предвзятые мнения к клочья. Любой ответ, который находит даже самую тривиальную уязвимость или учит меня новой идее будет высоко ценится.

Чтение из потока:

Согласно Учебное пособие по программированию GNU C GetLine:

Функция GetLine автоматически увеличит блок памяти по мере необходимости, через функцию Realloc, поэтому никогда не бывает нехватки места - одна из причин, по которой GetLine настолько безопасна. [..] Обратите внимание, что GetLine может безопасно обрабатывать вашу линию ввода, независимо от того, сколько времени это.

Я предполагаю, что getline должен, под всеми входами, предотвратить а переполнение буфера от того, что происходит при чтении из потока.

  • Мое предположение правильно? Существуют ли входные данные и/или схемы распределения, в которых это может привести к эксплойту? Например, если первый персонаж из потока Причудливый контрольный персонаж, может быть, 0x08 Backspace (CTL-H).
  • Была ли сделана какая -либо работа по математически доказывать GetLine как безопасную?

Malloc возвращает NULL при неудаче:

Если Malloc встречается с ошибкой Malloc возвращает нулевый указатель. Это представляет риск безопасности, поскольку можно применить арифметику указателя к нулевым (0x0) указателю, таким образом, Википедия рекомендует

/* Allocate space for an array with ten elements of type int. */
int *ptr = (int*)malloc(10 * sizeof (int));
if (ptr == NULL) {
    /* Memory could not be allocated, the program should handle 
       the error here as appropriate. */
} 

Secure SSCANF:

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

const char *inputStr = "a01234b4567c";
const char *formatStr = "a%[0-9]b%[0-9]c":
char *str1[strlen(inputStr)];
char *str2[strlen(inputStr)];

sscanf(inputStr, formatStr, str1, str2);

Поскольку str1 и str2 - это размер Inputstr, а не больше символов, чем strlen (inputstr) можно прочитать от inputstr, это кажется невозможным, дано Все возможные значения Для того, чтобы Inputstr вызвал переполнение буфера?

  • Я прав? Есть ли странные угловые дела, о которых я не думал?
  • Есть ли лучшие способы написать это? Библиотеки, которые уже решили это?

Основные вопросы:

Хотя я опубликовал большое количество вопросов, я не ожидаю, что кто -то ответит на все из них. Вопросы - это больше руководства для ответов, которые я ищу. Я действительно хочу узнать безопасное мышление C.

  • Какие еще безопасные идиомы C
  • Какие угловые чехлы мне нужно Всегда проверяйте?
  • Как я могу написать модульные тесты, чтобы обеспечить эти правила?
  • Как я могу применять ограничения в тестируемости или, что обеспечено правильным,?
  • Любая рекомендованная статическая/динамическая техника или инструменты для C?
  • Какими безопасными практиками C вы следуете и как вы оправдываете их для себя и других?

Ресурсы:

Многие из ресурсов были заимствованы из ответов.

Это было полезно?

Решение

  1. Чтение из потока

Дело в том, что getline() «Будет автоматически увеличить блок памяти по мере необходимости» означает, что это может использоваться в качестве атаки отказа в службе услуги, так как было бы тривиально генерировать вход, который был настолько длинным, что он исчерпал бы доступную память для процесса (или Хуже того, система!). После того, как возникает состояние вне памяти, другие уязвимости также могут вступить в игру. Поведение кода в памяти с низким/без/нет редко приятно, и его очень трудно предсказать. ИМХО безопаснее установить разумные верхние границы на все, особенно в чувствительных к безопасности приложений.

Кроме того (как вы ожидаете, упомянув специальных персонажей), getline() только дает вам буфер; Он не дает никаких гарантий о содержании буфера (поскольку безопасность полностью зависит от приложения). Таким образом, дезинфицирование ввода по -прежнему является неотъемлемой частью обработки и проверки пользовательских данных.

  1. SSCANF

Я хотел бы предпочесть использовать библиотеку регулярного выражения и иметь очень узко определенные REGEXP для пользовательских данных, а не использовать sscanf. Анкет Таким образом, вы можете выполнить большую проверку во время ввода.

  1. Общие комментарии

    • Доступны инструменты для пузырьков, которые генерируют случайный вход (как действительный, так и недействительный), которые можно использовать для проверки вашей входной обработки
    • Управление буфером имеет решающее значение: переполнение буфера, недостатки, вне памяти
    • Условия гонки могут быть использованы в ином случае безопасного кода
    • Двоирные файлы можно манипулировать для инъекции неверных значений или негабаритных значений в заголовки, поэтому код формата файлов должен быть каменным и не предполагать, что двоичные данные действительны.
    • Временные файлы часто могут быть источником проблем безопасности и должны быть тщательно управлялись
    • Внедрение кода может использоваться для замены системных или библиотечных вызовов среды на злонамеренных версиях
    • Плагины обеспечивают огромный вектор для атаки
    • В качестве общего принципа я бы предложил иметь четко определенные интерфейсы, когда пользовательские данные (или любые данные извне приложения) предполагаются недействительными и враждебными до тех пор, пока они не будут обработаны, дезинфицированы и проверены, и единственный способ для пользовательских данных ввести приложение

Другие советы

Я думаю, что ваш пример SSCANF неверен. Он все еще может переполняться при использовании таким образом.

Попробуйте это, что указывает максимальное количество байтов для чтения:

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%255s", &buf);
}

Взгляните на эту статью IBM Dev о защите от переполнений буфера.

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

Хорошее место, чтобы начать смотреть на это Отличный надежный сайт Дэвида Уилера.

Его бесплатная онлайн -книга "Безопасное программирование для Linux и Unix Howto"Это отличный ресурс, который регулярно обновляется.

Вы также можете посмотреть на его превосходный статический анализатор Плохой Чтобы получить дальнейшие намеки. Но помните, что ни один автоматизированный инструмент не является заменой хорошей пары опытных глаз, или, как Дэвид так красочно говорит.

Любой статический инструмент анализа, такой как Frawfinder, является просто инструментом. Ни один инструмент не может заменить человеческую мысль! Короче говоря, "Дурак с инструментом все еще дурак". Анкет Ошибка думать, что инструменты анализа (например, Frawfinder) являются заменой обучения и знаний безопасности

Я лично использовал ресурсы Дэвида уже несколько лет и нахожу их превосходными.

Янник Мой разработал самую слабую систему предварительного условия Hoare/Floyd для C во время доктора философии и применил его в библиотеку управляемых строк CERT. Анкет Он нашел ряд ошибок (см. Стр. 197 его мемуаров). Хорошей новостью является то, что библиотека теперь безопаснее для его работы.

Вы также можете посмотреть на веб -сайт Les Hatton здесь и в его книге Безопаснее c который вы можете получить от Amazon.

Не используйте gets() Для ввода используйте fgets(). Анкет Использовать fgets(), если ваш буфер автоматически распределен (т.е. «в стеке»), используйте эту идиому:

char buf[N];
...
if (fgets(buf, sizeof buf, fp) != NULL)

Это будет продолжать работать, если вы решите изменить размер buf. Анкет Я предпочитаю эту форму:

#define N whatever
char buf[N];
if (fgets(buf, N, fp) != NULL)

Потому что в первой форме используется buf определить второй аргумент и яснее.


Проверьте возвращаемое значение fclose().


Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top