[Информационная безопасность, Программирование, C++] Грехи C++ (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В мире разработки программного обеспечения мы ежедневно сталкиваемся с новыми угрозами кибербезопасности, а риски и последствия небезопасного программного обеспечения слишком велики, чтобы о них можно было не задумываться.
Давайте рассмотрим некоторые распространенные угрозы безопасности, которые могут таиться в нашем коде C/C++.
Эта статья представляет собой адаптированную версию презентации Мэри Келли при поддержке Embarcadero.
Мэри — опытный разработчик приложений с подтвержденным опытом работы в индустрии компьютерного программного обеспечения. Имеет опыт в C++, Delphi, базах данных, предпродажной подготовке и техническом писании. Сильный инженер со степенью бакалавра физики Университета штата Айова. Ее профиль на Linkedin и в других блогах на Embarcadero.
Что такое безопасность программного обеспечения
Чтобы заложить основу для нашего сегодняшнего обсуждения, давайте взглянем на определение безопасности:
Согласно Techopedia:
Безопасность программного обеспечения — это идея, реализованная для защиты программного обеспечения от злонамеренных атак и других рисков, чтобы программное обеспечение продолжало правильно функционировать при таких потенциальных рисках. Безопасность необходима для обеспечения целостности, аутентификации и доступности.
Важность безопасности программного обеспечения
- Меньше вероятность взлома данных
- Безопасность клиентов
- Репутация
- Вопросы соответствия/Нормативно-правовая база/Закон
- Возможная потеря дохода
- Легче поддерживать
Я хотел бы подчеркнуть последний пункт: легче поддерживать. Обнаружить ошибки безопасности очень сложно, поскольку они могут быть неочевидными и часто связаны с крайними случаями вашей бизнес-логики. Написание безопасного кода с самого начала сокращает время, необходимое для поиска и исправления этих ошибок.
Переполнение буфера
Это могут быть самые распространенные проблемы, которые в прошлом приводили к различным серьезным ошибкам.
Кратко:
- у вас есть буфер размером N
- вы получаете некоторые входные данные размера M
- вы записываете данные в буфер, не проверяя размер, если M < N.
Например, если ваш пароль может содержать не более 28 символов, хакеры могут воспользоваться этим и отправить вам:
helloworldthisisfirst28charsrundll
Если вы не проверяете длину строки, есть вероятность, что дополнительная часть входного сообщения просочится в соседнюю память вашей программы.
В большинстве серьезных случаев вы можете добавить некоторую дополнительную полезную нагрузку, которая выполняет системный вызов и вызовет root shell!
Ниже приведен фрагмент типичного «старого» переполнения буфера:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
char password[28];
char otherImportantBuffer[100] = { 'a'};
printf("Enter your password: ");
scanf("%s", password);
printf("your secret: %s\n", password);
}
Попробуйте передать более 28 символов.
В лучшем случае вы получите серьезный сбой или необработанную исключительную ситуацию. Но есть вероятность, что буфер «съест» часть памяти.
К счастью, такой код сложно скомпилировать даже на современных компиляторах! Это связано с тем, что различные «безопасные» альтернативы таким функциям, как scanf, gets или strcpy, требуют, чтобы вы передавали длину.
Вот несколько исправлений при переполнении буфера:
- Используйте новейшие компиляторы и библиотеки — они предлагают обновленные исправления безопасности и наиболее безопасную версию используемых вами функций.
- Используйте стандартную библиотеку C++ и STL
- Используйте библиотеки, которые проверяют границы
- Для переполнения буфера существует популярный метод, называемый нечетким тестированием. Fuzz-тестирование, или фаззинг, как его называют во многих кругах, — это метод тестирования, с помощью которого вы проверяете свои входные данные с помощью сгенерированных полурандомизированных значений, помогающих обеспечить стабильность и производительность ваших приложений. Я упомянул одну библиотеку фаззинга, которую я использую, под названием libFuzzer.
А вот отличное объяснение Heartbleed — страшная ошибка в OpenSSL, затронувшая миллионы пользователей: https://www.youtube.com/watch?v=1dOCHwf8zVQ.
Короче говоря, это вариант сценария переполнения буфера, в котором мы передаем меньше, чем фактический размер буфера. Это заставляет сервер отвечать данными, которые могут находиться за пределами буфера, и мы можем скрыть некоторую различную информацию о программе.
Проблемы с форматированием строк
Другая проблема исходит от функций, подобных printf, вот код:
void vulnerable() {
char buffer[60];
if (fgets(buffer, sizeof (buffer), stdin) == NULL)
return;
printf(buffer);
}
void notVulnerable () {
char buffer[60];
if (fgets(buffer, sizeof (buffer), stdin) == NULL)
return;
printf ("%s", buffer);
}
Какая функция безопаснее?
Основная проблема здесь в том, что если буфер содержит некоторые дополнительные символы строки формата и мы не проверяем его, можно добавить дополнительные инструкции и выполнить их. В случае notVulnerable() мы можем печатать только строки, поэтому дополнительный код не может быть вызван.
Рекомендуемые правки:
- Не передавайте вводимые пользователем данные непосредственно в качестве строки формата в функции форматирования.
- Используйте строки фиксированного формата или строки формата из надежного источника
- Следите за предупреждениями и ошибками компилятора
- Если необходимо использовать строки формата, используйте: printf («%s», user_input)
- Еще лучше не использовать семейство функций printf. Используйте потоковые операции, такие как std::cout или std::format (C++ 20) — они безопасны для типов.
Целочисленные переполнения
Целочисленное переполнение происходит, когда результат операции превышает допустимое максимальное значение для типа данных операции, и может вызвать сбои, логические ошибки, повышение привилегий и выполнение произвольного кода.
Вы можете сделать несколько простых правок:
- Изучите и поймите свой код. Поработайте немного с математикой!
- Проверьте все используемые вычисления, чтобы убедиться, что выделенная память и индексы массивов не могут переполниться.
- Используйте беззнаковые переменные для смещений массива и размеров для выделения памяти
- Обратите внимание на предупреждения вашего компилятора
- Проверьте наличие усечения и проблем с подписью при работе с size_t
- Опять же, C++ 20 улучшает функциональность с помощью функций безопасного интегрального сравнения в C++ 20.
Массив new и delete
Когда вы пишете что-то новое в своих приложениях, вы создаете неуправляемые объекты, а затем вам необходимо вызывать удаление, если вы не хотите рисковать утечками. Так что вообще не используйте new и delete, так как это считается плохой практикой в C++. Более того, работа в современном C++ позволяет использовать интеллектуальные указатели и классы контейнеров стандартной библиотеки, которые упрощают сопоставление каждого new с delete.
См. Рекомендации C++ Core — R.11: Избегайте явного вызова new и delete.
Плохая обработка ресурсов
В C ++ конструктор копирования вызывается, когда из объекта создается новая переменная. Если вы не создаете конструктор копирования, ваш компилятор сгенерирует конструктор копирования. Звучит здорово! Но если вы неправильно настроите конструктор, ошибки будут повторяться.
class PrtHolder {
public:
PtrHolder(void* p) : m_ptr(p) { }
~PtrHolder() {
delete m_ptr;
}
private:
void* m_ptr;
};
Когда ваш класс управляет ресурсами, вы должны объявить частный конструктор копирования и оператор присваивания без реализации (или использовать = delete); таким образом, если класс, внешний по отношению к классу с вашим частным объявлением, пытается вызвать один из них, вы получите ошибку компилятора о вызове частного метода. Даже если вы случайно позвоните одному из них, вы получите ошибку ссылки.
Инициализация указателя
Foo* pFoo;
if (GetFooPtr ( &pFoo ) )
{
// some code
}
// If pFoo is uninitialized, this is exploitable
pFoo->Release();
Чтобы избежать проблем с указателем, можно использовать несколько методов. Используйте эти шаги в C++:
- Инициализировать указатели, когда вы их объявляете — вроде несложно, но отличный способ упростить отладку вашего приложения, вместо того, чтобы беспокоиться о каком-то ранее используемом значении указателя.
- Нулевые указатели после использования
- Чтобы избежать утечек памяти, выделите память из кучи и верните ее на том же уровне абстракции.
- Возврат блоков в кучу, пока ваши указатели все еще находятся в области видимости
- Убедитесь, что типы указателей совпадают
Недостаток знаний STL
Знайте стандарты C++.
Есть замечательная группа людей, которые придумывают правила, касающиеся эволюции языка C++. Начиная с C++ 11, появляется все больше функций, которые помогают избежать многих ловушек, связанных с безопасностью вашего кода на C++. Чтобы узнать больше о C++ STL или стандартной библиотеке C++, я рекомендую посетить cppreference.com.
Вся презентация
Посмотреть всю презентацию Марии можно здесь:
Извините, данный ресурс не поддреживается. :(
Полезные ресурсы
- Writing Secure Code, Second Edition by Michael Howard and David LeBlanc
- 24 Deadly Software Security Sins: Programming Flaws and How to Fix Them by Michael Howard, David LeBlanc, John Viega
- Software Security: Building Security In by Gary McGraw
- Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) by Scott Meyers
- STL Tutorial and Reference Guide by David Musser
- C++ Core Guidelines
- Binary Exploitation — Buffer Overflow Explained in Detail — 0xRick
===========
Источник:
habr.com
===========
===========
Автор оригинала: Mary Kelly
===========Похожие новости:
- [Программирование, Работа с 3D-графикой, Алгоритмы, Визуализация данных] Майнкрафт для геологов: 3D-рендеринг миллиарда ячеек на встроенной видеокарте (часть 2)
- [Программирование, Машинное обучение] SberCloud + Intel oneAPI = бесплатное облако для ML-разработчиков
- [Open source, Программирование, C++] Развитие проекта arataga: пара рефакторингов по результатам натурных испытаний
- [Информационная безопасность, Разработка под MacOS, Настольные компьютеры, Процессоры] В Apple M1 нашли уязвимость M1RACLES — возможна скрытая передача данных между приложениями, вплоть до видеопотока
- [Веб-дизайн, Habr, JavaScript, Программирование, HTML] Косяк колхозной простоты в коде веб-сайта Хабра
- [C++, GTK+] Gtk, OpenGL и все-все-все
- [Сетевые технологии, Сетевое оборудование] Пентест сети провайдера или почему не стоит доверять свои данные провайдерам в Узбекистане
- [Python, Программирование, Google App Engine] Как использовать GraphQL Federation для инкрементальной миграции с монолита (Python) на микросервисы (Go) (перевод)
- [Программирование, Разработка мобильных приложений, Тестирование мобильных приложений] Особенности тестирования Android без Google-сервисов
- [Высокая производительность, Разработка веб-сайтов, Программирование] Как создать архитектуру для работы с высокой нагрузкой вашего веб-проекта? (перевод)
Теги для поиска: #_informatsionnaja_bezopasnost (Информационная безопасность), #_programmirovanie (Программирование), #_c++, #_bezopasnost (безопасность), #_c++, #_informatsionnaja_bezopasnost (
Информационная безопасность
), #_programmirovanie (
Программирование
), #_c++
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:37
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В мире разработки программного обеспечения мы ежедневно сталкиваемся с новыми угрозами кибербезопасности, а риски и последствия небезопасного программного обеспечения слишком велики, чтобы о них можно было не задумываться. Давайте рассмотрим некоторые распространенные угрозы безопасности, которые могут таиться в нашем коде C/C++. Эта статья представляет собой адаптированную версию презентации Мэри Келли при поддержке Embarcadero. Мэри — опытный разработчик приложений с подтвержденным опытом работы в индустрии компьютерного программного обеспечения. Имеет опыт в C++, Delphi, базах данных, предпродажной подготовке и техническом писании. Сильный инженер со степенью бакалавра физики Университета штата Айова. Ее профиль на Linkedin и в других блогах на Embarcadero. Что такое безопасность программного обеспечения Чтобы заложить основу для нашего сегодняшнего обсуждения, давайте взглянем на определение безопасности: Согласно Techopedia: Безопасность программного обеспечения — это идея, реализованная для защиты программного обеспечения от злонамеренных атак и других рисков, чтобы программное обеспечение продолжало правильно функционировать при таких потенциальных рисках. Безопасность необходима для обеспечения целостности, аутентификации и доступности.
Важность безопасности программного обеспечения
Я хотел бы подчеркнуть последний пункт: легче поддерживать. Обнаружить ошибки безопасности очень сложно, поскольку они могут быть неочевидными и часто связаны с крайними случаями вашей бизнес-логики. Написание безопасного кода с самого начала сокращает время, необходимое для поиска и исправления этих ошибок. Переполнение буфера Это могут быть самые распространенные проблемы, которые в прошлом приводили к различным серьезным ошибкам. Кратко:
Например, если ваш пароль может содержать не более 28 символов, хакеры могут воспользоваться этим и отправить вам: helloworldthisisfirst28charsrundll
Если вы не проверяете длину строки, есть вероятность, что дополнительная часть входного сообщения просочится в соседнюю память вашей программы. В большинстве серьезных случаев вы можете добавить некоторую дополнительную полезную нагрузку, которая выполняет системный вызов и вызовет root shell! Ниже приведен фрагмент типичного «старого» переполнения буфера: #define _CRT_SECURE_NO_WARNINGS
#include <stdio.h> int main() { char password[28]; char otherImportantBuffer[100] = { 'a'}; printf("Enter your password: "); scanf("%s", password); printf("your secret: %s\n", password); } Попробуйте передать более 28 символов. В лучшем случае вы получите серьезный сбой или необработанную исключительную ситуацию. Но есть вероятность, что буфер «съест» часть памяти. К счастью, такой код сложно скомпилировать даже на современных компиляторах! Это связано с тем, что различные «безопасные» альтернативы таким функциям, как scanf, gets или strcpy, требуют, чтобы вы передавали длину. Вот несколько исправлений при переполнении буфера:
А вот отличное объяснение Heartbleed — страшная ошибка в OpenSSL, затронувшая миллионы пользователей: https://www.youtube.com/watch?v=1dOCHwf8zVQ. Короче говоря, это вариант сценария переполнения буфера, в котором мы передаем меньше, чем фактический размер буфера. Это заставляет сервер отвечать данными, которые могут находиться за пределами буфера, и мы можем скрыть некоторую различную информацию о программе. Проблемы с форматированием строк Другая проблема исходит от функций, подобных printf, вот код: void vulnerable() {
char buffer[60]; if (fgets(buffer, sizeof (buffer), stdin) == NULL) return; printf(buffer); } void notVulnerable () { char buffer[60]; if (fgets(buffer, sizeof (buffer), stdin) == NULL) return; printf ("%s", buffer); } Какая функция безопаснее? Основная проблема здесь в том, что если буфер содержит некоторые дополнительные символы строки формата и мы не проверяем его, можно добавить дополнительные инструкции и выполнить их. В случае notVulnerable() мы можем печатать только строки, поэтому дополнительный код не может быть вызван. Рекомендуемые правки:
Целочисленные переполнения Целочисленное переполнение происходит, когда результат операции превышает допустимое максимальное значение для типа данных операции, и может вызвать сбои, логические ошибки, повышение привилегий и выполнение произвольного кода. Вы можете сделать несколько простых правок:
Массив new и delete Когда вы пишете что-то новое в своих приложениях, вы создаете неуправляемые объекты, а затем вам необходимо вызывать удаление, если вы не хотите рисковать утечками. Так что вообще не используйте new и delete, так как это считается плохой практикой в C++. Более того, работа в современном C++ позволяет использовать интеллектуальные указатели и классы контейнеров стандартной библиотеки, которые упрощают сопоставление каждого new с delete. См. Рекомендации C++ Core — R.11: Избегайте явного вызова new и delete. Плохая обработка ресурсов В C ++ конструктор копирования вызывается, когда из объекта создается новая переменная. Если вы не создаете конструктор копирования, ваш компилятор сгенерирует конструктор копирования. Звучит здорово! Но если вы неправильно настроите конструктор, ошибки будут повторяться. class PrtHolder {
public: PtrHolder(void* p) : m_ptr(p) { } ~PtrHolder() { delete m_ptr; } private: void* m_ptr; }; Когда ваш класс управляет ресурсами, вы должны объявить частный конструктор копирования и оператор присваивания без реализации (или использовать = delete); таким образом, если класс, внешний по отношению к классу с вашим частным объявлением, пытается вызвать один из них, вы получите ошибку компилятора о вызове частного метода. Даже если вы случайно позвоните одному из них, вы получите ошибку ссылки. Инициализация указателя Foo* pFoo;
if (GetFooPtr ( &pFoo ) ) { // some code } // If pFoo is uninitialized, this is exploitable pFoo->Release(); Чтобы избежать проблем с указателем, можно использовать несколько методов. Используйте эти шаги в C++:
Недостаток знаний STL Знайте стандарты C++. Есть замечательная группа людей, которые придумывают правила, касающиеся эволюции языка C++. Начиная с C++ 11, появляется все больше функций, которые помогают избежать многих ловушек, связанных с безопасностью вашего кода на C++. Чтобы узнать больше о C++ STL или стандартной библиотеке C++, я рекомендую посетить cppreference.com. Вся презентация Посмотреть всю презентацию Марии можно здесь: Извините, данный ресурс не поддреживается. :( Полезные ресурсы
=========== Источник: habr.com =========== =========== Автор оригинала: Mary Kelly ===========Похожие новости:
Информационная безопасность ), #_programmirovanie ( Программирование ), #_c++ |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:37
Часовой пояс: UTC + 5