Русский
Русский
English
Статистика
Реклама

С безопасность для новичков

Привет, хабровчане. Для будущих студентов курса "C++ Developer. Professional" Александр Колесников подготовил статью.

Приглашаем также посмотреть открытый вебинар на тему
Области видимости и невидимости. За 1,5 часа участники вместе с экспертом успеют реализовать класс общего назначения и запустить несколько unit-тестов с использованием googletest. Присоединяйтесь.


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

Сегодня язык программирования С++ существует в нескольких параллельных реальностях: C++98, C++11, C++14, C++17, C++20. Существует как минимум один источник, где можно немного разобраться со всем этим набором мультивселенных. Однако, когда дело дойдет до написания кода использования stackOverflow, вопрос а точно эта строка написана безопасно", будет мучать разработчика из релиза в релиз. Кстати, на момент написания статьи готовится новый стандарт С++23 =).

Откуда проблемы

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

Самые распространенные проблемы, с которыми может столкнуться новичок:

  • неверное объявление типов данных;

  • неверное использование выражений;

  • неправильная обработка целочисленных данных;

  • неправильная работа с контейнерами;

  • неправильная работа со строками;

  • неправильная работа с памятью;

  • неверная обработка exception;

  • пренебрежение ограничениями OOP;

  • состояния гонки при обработке ресурсов мультипоточным приложением;

  • прочие проблемы, о которых мало, где сказано.

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

Разберем несколько примеров.

String Format

void check_password(const char *user) {  int ret;  //  static const char format[] = "%s wrong pass.\n";  size_t messageLength = strlen(user) + sizeof(msg_format);  char *data = (char *)malloc(messageLength); // <- так же не очень безопасный вариант  if (data == NULL) {    //Код для ошибки  }  ret = snprintf(data, messageLength, format, user);  if (ret < 0) {     //Код для ошибки  } else if (ret >= messageLength) {     //Последний шанс обработать некорректные данные  }  syslog(LOG_INFO, msg);  free(msg);}

В чем проблема? Данные, которые используются для создания строки, контролируются пользователем. Передача других спец символов (%n, %x) и использование строк больших размеров может вывести из строя приложение.

Integer overflow

Данная проблема головная боль любого ПО, которое работает с накапливаемыми данными. Какого размера переменные использовать, чтобы оптимально хранить данные и одновременно сделать запас для приложения, если если оно будет использоваться месяцами без перезапуска? В некоторых случаях ответить однозначно на этот вопрос нельзя, поэтому программист, ориентируясь на собственный опыт, волевым решением пишет uint16_t. Но данных оказывается больше 65,535 и тут случается чудо значение переменной становится равно нулю и отсчет идёт заново. Пример кода:

...user->nameLength = getUserNameLength(&user->name) ;user->newDbCellLen = malloc(user->nameLength * sizeof(uint8_t))...

Одна строка и один выстрел в голову всему приложению. Теперь пользователь может спокойно выделять столько памяти, сколько ему нужно. При этом приложение продолжит какое-то время работать. Исправить можно с помощью простой функции:

...int16_t checkLen(uint16_t firstNumber, uint16_t secondNumber){    uint16_t resultLength;    if (UINT_MAX - firstNumber < secondNumber)     {        //ошибка        return -1;    }    else    {        resultLength = firstNumber + secondNumber;    }    return resultLength;}

Преобразование типов

Если вас не пугают сложности и вы все-таки хотите развиваться как программист С++, то скорее всего к вам в руки попадет код, который разрабатывался Когда динозавры под стол пешком ходили и в нем будет много кода, аналогичному приведенному ниже:

...unsigned int number = (unsigned int)ptr;number = (number & 0x7fffff) | (flag << 23);ptr = (char *)number;...

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

Выводы

Как видно из примеров в статье, С++ это язык, в котором нужно внимательно относиться к кажущимся мелочам, начиная от форматов данных и заканчивая типами переменных. Где брать примеры? Что делать с уязвимостями? К сожалению, универсального ответа нет, но можно постоянно работать и накапливать знания о языке и его особенностях. Начать можно здесь или здесь. Так же нужно использовать плагины и приложения, которые позволяют анализировать код на этапе сборки. Можно в этом случае ориентироваться на продукты вроде этого или этого.


Узнать подробнее о курсе "C++ Developer. Professional".

Смотреть открытый вебинар на тему Области видимости и невидимости.

Источник: habr.com
К списку статей
Опубликовано: 05.03.2021 18:07:04
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus. онлайн-образование

Информационная безопасность

Программирование

C++

Programming

Cplusplus

Область видимости

Категории

Последние комментарии

© 2006-2021, personeltest.ru