О каких рисках / уязвимостях безопасности должен знать каждый программист C? [закрыто]

13

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

Каковы риски или уязвимости (например, переполнение буфера), о которых должен знать каждый программист C (уязвимости IE, относящиеся к программистам C)? К каким проблемам это может привести? Как их избежать и каковы распространенные ошибки, вызывающие их появление в программах?

Анто
источник
Как насчет этого списка: owasp.org/index.php/Category:OWASP_Top_Ten_Project Что еще нужно, кроме этого?
С.Лотт
2
@ S.Lott: Похоже, что очень много о проблемах безопасности в веб-разработке. Похоже, что в целом ресурсов на это больше, чем того, о чем я на самом деле прошу.
Anto
@ Анто: Пожалуйста, обновите вопрос, чтобы различить все ресурсы по безопасности и безопасности, о которых вы спрашиваете.
С.Лотт
@ S.Lott: Я не уверен, что вы имеете в виду. Я прошу о безопасности, которая важна для большинства программистов на Си, то есть такие вещи, как переполнение буфера и другие вещи, которые возможны в C.
Anto
@ Анто: «Похоже, что в целом [веб-безопасности?] Ресурсов больше, чем то, о чем я на самом деле прошу». Похоже, вы спрашиваете о какой-то безопасности, которая не является веб-безопасностью. Правда? Если это так, пожалуйста, обновите вопрос, чтобы объяснить, что вы ищете. Ложь? Затем будут просить о веб - безопасности, в этом случае, почему не список OWASP упоминается в вашем вопросе?
С.Лотт

Ответы:

13

Переполнение буфера является большим. По умолчанию в C ничего не проверяется по диапазону, поэтому очень просто перезаписать буфер. Существует стандартная библиотечная функция, gets()которую нельзя остановить от переполнения буфера, и ее почти никогда не следует использовать.

Существуют некоторые методы уровня реализации, препятствующие эксплуатации, такие как скремблирование блоков кучи, но это не остановит переполнения буфера в локальных буферах, что часто может делать интересные вещи, такие как изменение адреса, к которому будет возвращаться функция.

В Си нет хорошего общего решения. Многие библиотечные функции имеют версии, которые будут ограничивать объем, который они будут писать. хотя подсчитывать это может быть неуклюже. Есть программное обеспечение, которое может обнаружить переполнение буфера динамической памяти в тесте, если выполняется соответствующий тест, и переполнение стека часто будет отображаться как сбой в тестировании. Помимо этого, это вопрос тщательного кодирования и проверки кода.

С этим связана проблема записи в буфер, слишком маленький на один символ, забывая, что строка C длиной n символов требует n + 1 символов в памяти из-за '\0'ограничителя. Если злоумышленнику удастся сохранить строку без терминатора, любая функция C, ожидающая строку, продолжит обработку до тех пор, пока не достигнет нулевого байта, что может привести к копированию или выводу большего количества информации, чем необходимо (или попаданию в защищенную память для атаки DOS). ). Решением, опять же, является осознание, забота и проверка кода.

Есть еще один риск с printf()семьей. Если вы когда-либо пишете char * str; ... printf(str);, вы настраиваете себя на проблемы, если strв распечатке содержится символ «%». %nДиректива формат позволяет printf()записать в память. Решение printf("%s", str);или puts(str);. (Кроме того, используйте C99 snprintf()вместо sprintf().)

Использование целых чисел без знака, особенно в качестве индексов цикла, может вызвать проблемы. Если вы назначите небольшое отрицательное значение без знака, вы получите большое положительное значение. Это может подорвать такие вещи, как обработка только N экземпляров чего-либо или ограниченные функции, например strncpy(). Изучите все целые числа без знака. Возможно, вы захотите избежать unsigned short, так как большое значение в одном из них преобразуется в большое положительное значение в int.

Не забывайте, что символьная константа в C на самом деле является int. Написание чего-то подобного char c; while((c = getchar()) != EOF) ...может легко потерпеть неудачу, так EOFкак не будет представимо в char.

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

Дэвид Торнли
источник
Там нет необходимости использовать printf("%s", str)для голой строки, когда puts(str)будет делать ту же работу.
Blrfl
@Blrfl, но putsдобавляет символ новой строки, пока printfнет.
правый раз
Могли бы и сделать fputs(str, stdout), чего нет.
Blrfl
Что касается целочисленного переполнения: использование подписанных целых чисел не является решением, так как переполнение их вызовет UB. Единственное (болезненное) решение состоит в том, чтобы либо формально доказать, что вы никогда не будете переполняться, либо проверять во время выполнения (но проверять правильно, что также сложно, без переполнения в проверке).
слеске
@DavidThornley: стандартно удаленные функции C11 и C ++ 14 получают функцию () из стандартной библиотеки из-за ее опасности.
Destructor
5

Некоторые из специфических для C рисков включают: переполнение буфера , форматирование строковых атак и целочисленные переполнения .

Неманья Трифунович
источник
1
Нет ничего специфичного для C в переполнении буфера - это может иметь любой язык с указателями. Целочисленные переполнения применимы практически к любому языку и могут легко происходить в управляемом коде.
Стив
1
@ Стив, на самом деле это не указатели, которые вызывают эту проблему, а то, как язык не устанавливает границы массивов.
Дуг Т.
2
@ Задайте вопрос не о вещах, которые касаются только C, а о том, что программисты на C должны знать.
AttackingHobo
1
@Steve: C необычно восприимчив к переполнению буфера, отчасти из-за отсутствия поддержки проверки диапазона и количества библиотечных функций, которые будут радостно переполнять буферы для вас.
Дэвид Торнли
Я понимаю, что вопрос касается, в частности, C, но я думаю, что стоит уточнить, если ответ зачитывается вне контекста, что эти риски носят более общий характер. В частности, разработчики управляемого кода (IMHO) слишком удовлетворены безопасностью, и целочисленные переполнения, в частности, влияют на большинство языков.
Стив
4

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

Рассмотрим следующий код, который будет компилироваться без проблем.

if(lpstr_current_state = CONST_EMERGENCY_STATE_HOLY_CRAP)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}

Когда вы проверяете, если lpstr_current_stateв CONST_EMERGENCY_STATE_HOLY_CRAPсамом деле вы назначая. Лучше всегда ставить постоянную переменную слева. Если вы поместите константу слева, то компилятор потерпит неудачу, потому что вы не можете присвоить значение переменной.

if(CONST_EMERGENCY_STATE_HOLY_CRAP = lpstr_current_state)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}

Тогда вы можете легко сказать себе: «Черт возьми, это могло быть плохо», исправляя код для чтения ...

if(CONST_EMERGENCY_STATE_HOLY_CRAP == lpstr_current_state)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}
Кристофер Хох
источник
7
Это легко для компилятора поймать и пометить как предупреждение, в отличие от некоторых других проблем. К сожалению, не все компиляторы делают это легко.
Дэвид Торнли
2
Это может происходить на языках, отличных от C, на любом языке, который использует =и ==.
FrustratedWithFormsDesigner
3
Это также не является уязвимостью безопасности, это ошибка.
Крис Питман
1
Это так называемые условия йоды.
2
@Kristofer Hoch: Если мы собираемся назвать любую вероятную ошибку C риском, и рассмотрим ее здесь, нам понадобится гораздо больший форум.
Дэвид Торнли
0

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

Поэтому, когда вы думаете, что «никто в здравом уме не будет ...», вам нужно немедленно подумать «за исключением того, кто хочет взломать компьютеры других людей, сделает именно это».

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

gnasher729
источник
Хотя я бы согласился с абзацами вторым и третьим, возложить всю вину на нападавшего мне немного не по себе. Для успешной атаки всегда нужны два: программист, который облажался, и злоумышленник, который поймал программиста на месте. Однако уязвимость в системе безопасности существует еще до того, как злоумышленник сможет ее использовать. И в этом виноват программист.
cmaster - восстановить монику