"Средний человек не хочет быть свободным. Он просто хочет быть в безопасности". - H. L. Menken
Я пытаюсь записать очень безопасный C. Ниже я перечисляю некоторые методы, которые я использую и спрашиваю, они столь безопасный, как я думаю, что они. Не не смущайтесь рвать мой код/предвзятые мнения к клочкам. Любой ответ, который находит даже самую тривиальную уязвимость или преподает мне новую идею, будет высоко оценен.
Чтение из потока:
Согласно GNU C Программирование Учебного руководства getline:
Функция getline автоматически увеличит блок памяти по мере необходимости через функцию перевыделения, таким образом, никогда не будет нехватки пространства - одна причина, почему getline так безопасен [..] Заметьте, что getline может безопасно обработать Вашу строку входа, неважно, какой длины это.
Я предполагаю, что getline, под всеми исходными данными, должен препятствовать тому, чтобы переполнение буфера произошло при чтении из потока.
- Мое предположение корректно? Есть ли исходные данные и/или схемы выделения, в соответствии с которыми это могло привести к использованию? Например, что, если первый символ от потока является некоторым причудливым управляющим символом, возможно, 0x08 КЛАВИША BACKSPACE (ctl-H).
- Какая-либо работа была сделана для математического доказательства getline как безопасного?
Пустой указатель возвратов Malloc при отказе:
Если 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. */ }
Безопасный 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 Вы применяете и как Вы выравниваете по ширине их себе и другим?
Ресурсы:
Многие ресурсы были заимствованы из ответов.
- Безопасное программирование для ПРАКТИЧЕСКОГО РУКОВОДСТВА Linux и Unix David Wheeler
- Защитите C, программирующий - Sun Microsystems
- Небезопасное программирование примером
- Добавьте больше NOPS - блог, охватывающий эти проблемы
- CERT безопасная инициатива кодирования
- flawfinder - инструмент статического анализа
- Используя Программы автоматического доказательства Thm для доказательства безопасности Yannick Moy
- libsafe
]Тот факт, что []getline()[
] "будет автоматически увеличивать блок памяти по мере необходимости" означает, что это может быть использовано в качестве атаки типа "отказ в обслуживании", так как было бы тривиально сгенерировать вход, который был бы настолько длительным, что исчерпал бы доступную для процесса память (или, что еще хуже, систему!). При возникновении состояния вне памяти могут появиться и другие уязвимости. Поведение кода в условиях нехватки/отсутствия памяти редко бывает приятным, и его очень трудно предсказать. IMHO безопаснее устанавливать разумные верхние границы на всем, особенно в приложениях, чувствительных к безопасности.[
]Кроме того (как вы и предполагали, упоминая специальные символы), []getline()[
] дает только буфер; это не дает никаких гарантий относительно содержимого буфера (так как безопасность полностью зависит от приложения). Поэтому дезинфекция входных данных по-прежнему является существенной частью обработки и проверки пользовательских данных.[
]sscanf[] я бы предпочёл использовать библиотеку регулярных выражений, а не использовать []sscanf[
], а иметь очень узко определенные регеxps для пользовательских данных. Таким образом, вы можете выполнять валидацию во время ввода.[
]Общие комментарии[
] [Янник Мой во время защиты кандидатской диссертации разработал самую слабую систему предварительных условий для языка C и применил ее к библиотеке управляемых строк CERT . Он обнаружил ряд ошибок (см. стр. 197 его мемуаров). Хорошая новость заключается в том, что библиотека теперь более безопасна для его работы.
.Я думаю, твой пример с сканированием неправильный. Он все еще может переполниться при таком использовании.
Попробуйте это, которое определяет максимальное количество байт для чтения:
void main(int argc, char **argv)
{
char buf[256];
sscanf(argv[0], "%255s", &buf);
}
Взгляните на эту статью разработчиков IBM о защите от переполнения буфера.
С точки зрения тестирования, я бы написал программу, которая генерирует случайные строки произвольной длины и передает их в вашу программу, и убедился, что они обрабатываются соответствующим образом.
Хорошим местом для начала работы является отличный безопасный сайт кодирования Дэвида Уилера.
Его бесплатная онлайн-книга "Безопасное программирование для Linux и Unix HOWTO" является отличным ресурсом, который регулярно обновляется.
Вы также можете посмотреть на его отличный статический анализатор FlawFinder для получения дополнительных подсказок. Но помните, что ни один автоматический инструмент не заменит хорошую пару опытных глаз, или, как выразился Дэвид так красочно...
Любой инструмент статического анализа, такой как Flawfinder, является просто инструментом. Ни один инструмент не может заменить человеческую мысль! Короче говоря, "дурак с инструментом все еще дурак" . Ошибочно думать, что инструменты анализа (такие как Flawfinder) заменяют обучение и знания в области безопасности
Я лично использовал ресурсы Дэвида в течение нескольких лет и нахожу их превосходными.
Вы также можете посмотреть веб-сайт Леса Хаттона здесь и его книгу Safer C , которую можно получить на Amazon.
Не используйте get()
для ввода, используйте 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()
.