Запись безопасного C и безопасных идиом C

"Средний человек не хочет быть свободным. Он просто хочет быть в безопасности". - 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 Вы применяете и как Вы выравниваете по ширине их себе и другим?

Ресурсы:

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

41
задан Ethan Heilman 27 March 2012 в 20:58
поделиться

7 ответов

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

]Тот факт, что []getline()[] "будет автоматически увеличивать блок памяти по мере необходимости" означает, что это может быть использовано в качестве атаки типа "отказ в обслуживании", так как было бы тривиально сгенерировать вход, который был бы настолько длительным, что исчерпал бы доступную для процесса память (или, что еще хуже, систему!). При возникновении состояния вне памяти могут появиться и другие уязвимости. Поведение кода в условиях нехватки/отсутствия памяти редко бывает приятным, и его очень трудно предсказать. IMHO безопаснее устанавливать разумные верхние границы на всем, особенно в приложениях, чувствительных к безопасности.[

] [

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

] [
    ] [
  1. ]sscanf[
  2. ] [
] [

]sscanf[] я бы предпочёл использовать библиотеку регулярных выражений, а не использовать []sscanf[], а иметь очень узко определенные регеxps для пользовательских данных. Таким образом, вы можете выполнять валидацию во время ввода.[

] [
    ] [
  1. ][

    ]Общие комментарии[

    ] [
      ] [
    • ]Нечеткие инструменты, которые генерируют случайные входные данные (как действительные, так и недействительные), которые могут быть использованы для проверки работы буфера с входными данными[
    • ] [
    • ]Управление буфером имеет критическое значение: переполнения буфера, подземные потоки, условия гонки вне памяти[
    • ] [
    • ]Условия гонки могут быть использованы в другом безопасном коде[
    • ] [
    • ]Двоичные файлы могут быть манипулированы для впрыскивания недействительных значений или значений с большим размером в заголовки, Поэтому код формата файла должен быть твердым и не предполагать, что бинарные данные действительны[
    • ] [
    • ]Временные файлы часто могут быть источником проблем с безопасностью, и должны тщательно управляться[
    • ] [
    • ]Инъекция кода может быть использована для замены системных или библиотечных вызовов на вредоносные версии[
    • ] [
    • ]Плагины обеспечивают огромный вектор для атаки[
    • ] [
    • ]В качестве общего принципа, Я бы предложил иметь четко определенные интерфейсы, в которых пользовательские данные (или любые данные, полученные извне) считаются недействительными и враждебными до тех пор, пока они не будут обработаны, обезврежены и проверены, и единственный способ ввода пользовательских данных в приложение [
    • ] [
    ][
  2. ] [
]
4
ответ дан 27 November 2019 в 00:57
поделиться

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

.
1
ответ дан 27 November 2019 в 00:57
поделиться

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

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

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

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

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

6
ответ дан 27 November 2019 в 00:57
поделиться

Хорошим местом для начала работы является отличный безопасный сайт кодирования Дэвида Уилера.

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

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

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

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

4
ответ дан 27 November 2019 в 00:57
поделиться

Вы также можете посмотреть веб-сайт Леса Хаттона здесь и его книгу Safer C , которую можно получить на Amazon.

1
ответ дан 27 November 2019 в 00:57
поделиться

Не используйте 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().


0
ответ дан 27 November 2019 в 00:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: