Для получения триального ключа
заполните форму ниже
Team License (базовая версия)
Enterprise License (расширенная версия)
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
GBP
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Мне интересно попробовать плагин на:
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

** На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
Безопасность, безопасность! А вы её тес…

Безопасность, безопасность! А вы её тестируете?

07 Ноя 2012

В коде программ нет мест, где нельзя допустить ошибку. Ошибка может быть в самом простом месте. Если алгоритмы, механизмы обмена данными и интерфейсы люди привыкли тестировать, то с безопасностью всё обстоит гораздо хуже. Часто она реализуется по остаточному принципу. Программист думает, вот сейчас пару строк напишу, и всё будет хорошо. И даже тестировать не надо. Код слишком прост, чтобы допустить в нем ошибку! А вот и нет. Раз занимаетесь безопасностью и пишите какой-то код для этого, то тестируйте его не менее тщательно!

Где важна безопасность? Во многих приложениях. Но не будем рассуждать абстрактно. Давайте возьмём, например, исходные коды приложения Tor. Это свободное программное обеспечение для реализации второго поколения так называемой "луковой маршрутизации". Это система, позволяющая устанавливать анонимное сетевое соединение, защищённое от прослушивания. Подробнее что это такое и для чего используется, можно узнать, открыв статью в Википедии.

Казалось бы, в таком приложении надо быть максимально внимательным к безопасности данных. И не просто внимательным! Скажем так, такое приложение стоит разрабатывать, только находясь в состоянии паранойи и мании преследования.

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

Одним из механизмов страховки является обнуление буферов, которые больше не используются. В таких буферах могут содержаться пароли, ip-адреса и другие пользовательские данные. Если эти данные не уничтожать, то они в виде мусора потом могут быть отправлены в интернет. И это не сказки, а реалии жизни. О том, как такое может произойти, можно почитать в статье "Перезаписывать память - зачем?".

Разработчики TOR знают об этой опасности. И пытаются затирать содержимое буферов, используя функцию memset(). Это Epic Fail. Компилятор вправе убрать из кода вызов функции memset(), если заполняемый ею буфер более нигде не используется.

Рассмотрим фрагмент кода, взятый из TOR:

int
crypto_pk_private_sign_digest(....)
{
  char digest[DIGEST_LEN];
  ....
  memset(digest, 0, sizeof(digest));
  return r;
}

Смотрите. На стеке создается буфер 'digest'. Далее он используется. Не важно, как. Главное что в конце мы хотим его обнулить. Для этого написан вызов функции memset(). Однако, после этого буфер 'digest' более в функции никак не используется. При оптимизации компилятор заметит это и удалит вызов функции. Это не изменит логику работы программы. Но это сделает её опасной с точки зрения приватности данных.

Те, кого интересуют подробности, могут заглянуть сюда. Здесь приведён ассемблерный листинг, где показано как исчезает вызов функции memset(). В качестве компилятора используется Visual C++ 2010 и ключ "/O2".

Чтобы гарантированно затереть память следует использовать такие функции, как RtlSecureZeroMemory(). Они специально созданы для таких случаев и не могут быть удалены компилятором.

Вы скажете, что из мухи я делаю слона. И что никакие важные данные никуда не попадут. Возможно. Но кто даст гарантию? Раз разработчики сделали обнуление массива, то значит, они чего-то опасались. И делали они это не в одном, и не в двух местах. Много где. Жаль только, что усилия часто потрачены впустую. Чтобы не быть голословным, приведу список мест с ошибками.

Список файлов и строк, где анализатор PVS-Studio выдал предупреждение "V597 The compiler could delete the 'memset' function call, which is used to flush '...' buffer. The RtlSecureZeroMemory() function should be used to erase the private data":

  • crypto.c 1015
  • crypto.c 1605
  • crypto.c 2233
  • crypto.c 2323
  • tortls.c 2453
  • connection_or.c 1798
  • connection_or.c 2128
  • onion.c 295
  • onion.c 384
  • onion.c 429
  • rendclient.c 320
  • rendclient.c 321
  • rendclient.c 699
  • rendclient.c 942
  • rendclient.c 1284
  • rendclient.c 1285
  • rendservice.c 705
  • rendservice.c 900
  • rendservice.c 903
  • rendservice.c 904
  • rendservice.c 905
  • rendservice.c 906
  • rendservice.c 1409
  • rendservice.c 1410
  • rendservice.c 1411
  • rendservice.c 1412
  • rendservice.c 1413
  • rendservice.c 1414
  • rendservice.c 1415
  • rendservice.c 2078
  • rendservice.c 2079
  • rendservice.c 2080
  • rendservice.c 2516
  • rendservice.c 2517
  • rendservice.c 2518
  • rendservice.c 2668
  • rendservice.c 2669
  • rendservice.c 2670
  • tor-gencert.c 108

Я сознательно привел такой длинный список. Я хочу, чтобы вы осознали всю глубину проблемы отсутствия проверок кода, который должен позаботиться о безопасности. Кажется, ну как можно ошибиться, используя memset()!? Оказывается, ещё как можно.

И это проблема не только TOR. Это вообще проблема многих приложений и библиотек. Далеко ходить не надо. Какие библиотеки использует TOR? Например, он использует OpenSSL. Это криптографический пакет с открытым исходным кодом для работы с SSL/TLS. Давайте посмотрим, как очищают память разработчики OpenSSL.

Разработчики OpenSSL знают, что нельзя использовать memset() для обнуления буферов памяти. Поэтому, они написали свою функцию. Вот она:

unsigned char cleanse_ctr = 0;
void OPENSSL_cleanse(void *ptr, size_t len)
{
  unsigned char *p = ptr;
  size_t loop = len, ctr = cleanse_ctr;
  while(loop--)
  {
    *(p++) = (unsigned char)ctr;
    ctr += (17 + ((size_t)p & 0xF));
  }
  p=memchr(ptr, (unsigned char)ctr, len);
  if(p)
    ctr += (63 + (size_t)p);
  cleanse_ctr = (unsigned char)ctr;
}

Отличный параноидальный код. К нему претензий нет. Он затрет память. Причем не просто нулями, а случайными числами.

Вот только из-за ошибок, эта функция не спасает, и приватные данные останутся на своём месте. Посмотрим вот на этот код:

void usage(void)
{
  static unsigned char *buf=NULL,*obuf=NULL;
  ....
  OPENSSL_cleanse(buf,sizeof(buf));
  OPENSSL_cleanse(obuf,sizeof(obuf));
  ....  
}

Столько усилий было потрачено для написания функции OPENSSL_cleanse(). И всё мимо.

Присмотритесь. Не замечаете ничего плохого?

Выражения sizeof(buf) и sizeof(obuf) вычисляют размер указателя, а вовсе не размер буфера. В результате, в 32-битной программе в буфере будут затерты только первые 4 байта. Все остальные приватные данные стерты не будут.

Можно найти в OpenSSL и другие идентичные ошибки (см. V597):

  • ec_mult.c 173
  • ec_mult.c 176

Выводы:

  • Если безопасность данных является важной составляющей в вашем программном обеспечении, то вы должны реализовывать соответствующие тесты. Например, создавая юнит-тесты для функции, вы должны дополнительно проверить, что в стеке не осталась важных данных. Для этого можно вызвать функцию, вначале которой создать массив вида "char buf[10000]" и поискать в нём строки, которые могли остаться в стеке.
  • Тестируйте не только DEBUG версию, но и RELEASE. Рассмотренные в статье ошибка с memset() в DEBUG версии не проявят себя.
  • Используйте статические анализаторы кода. Они могут сказать очень много интересного про ошибки и небезопасные места в коде.
  • Критические с точки зрения приложения лучше делать открытыми. Сейчас я случайно забрел в открытый проект TOR и нашёл ошибки. Эту информацию можно использовать, чтобы сделать продукт лучше. А могу ли я проверить закрытый код? Нет. А значит, подобные ошибки могут оставаться незамеченными разработчиками годами.
  • Сколько бы у программиста не было опыта и стажа, он не застрахован от простых и глупых ошибок. Помните, что утверждение "профессиональные программисты не допускают простых ошибок и опечаток" это миф. Это не так. Лучше быть самокритичным. Уже одно осознание, что можно допустить ошибку, помогает избежать многих из них. Зная это, вы не поленитесь написать дополнительный тест, запустить анализатор кода и просто перечитать написанный код.

Популярные статьи по теме
Технологии, используемые в анализаторе кода PVS-Studio для поиска ошибок и потенциальных уязвимостей

Дата: 21 Ноя 2018

Автор: Андрей Карпов

Краткое описание технологий, используемых в инструменте PVS-Studio, которые позволяют эффективно обнаруживать большое количество паттернов ошибок и потенциальных уязвимостей. Статья описывает реализа…
PVS-Studio ROI

Дата: 30 Янв 2019

Автор: Андрей Карпов

Время от времени нам задают вопрос, какую пользу в денежном эквиваленте получит компания от использования анализатора PVS-Studio. Мы решили оформить ответ в виде статьи и привести таблицы, которые по…
Как и почему статические анализаторы борются с ложными срабатываниями

Дата: 20 Мар 2017

Автор: Андрей Карпов

В своей предыдущей статье я писал, что мне не нравится подход, при котором статические анализаторы кода оцениваются с помощью синтетических тестов. В статье приводился пример, воспринимаемый анализат…
Как PVS-Studio оказался внимательнее, чем три с половиной программиста

Дата: 22 Окт 2018

Автор: Андрей Карпов

PVS-Studio, как и другие статические анализаторы кода, часто выдаёт ложные срабатывания. Но не стоит спешить считать странные срабатывания ложными. Это короткая история о том, как PVS-Studio вновь ок…
Любите статический анализ кода!

Дата: 16 Окт 2017

Автор: Андрей Карпов

Я в шоке от возможностей статического анализа кода, хотя сам участвую в разработке инструмента PVS-Studio. На днях я был искренне удивлён тому, что анализатор оказался умнее и внимательнее меня.
PVS-Studio для Java

Дата: 17 Янв 2019

Автор: Андрей Карпов

В седьмой версии статического анализатора PVS-Studio мы добавили поддержку языка Java. Пришло время немного рассказать, как мы начинали делать поддержку языка Java, что у нас получилось и какие дальн…
Главный вопрос программирования, рефакторинга и всего такого

Дата: 14 Апр 2016

Автор: Андрей Карпов

Вы угадали, ответ - "42". Здесь приводится 42 рекомендации по программированию, которые помогут избежать множества ошибок, сэкономить время и нервы. Автором рекомендаций выступает Андрей Карпов - тех…
Зло живёт в функциях сравнения

Дата: 19 Май 2017

Автор: Андрей Карпов

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

Дата: 31 Май 2014

Автор: Андрей Карпов

Я изучил множество ошибок, возникающих в результате копирования кода. И утверждаю, что чаще всего ошибки допускают в последнем фрагменте однотипного кода. Ранее я не встречал в книгах описания этого …
Характеристики анализатора PVS-Studio на примере EFL Core Libraries, 10-15% ложных срабатываний

Дата: 31 Июл 2017

Автор: Андрей Карпов

После большой статьи про проверку операционной системы Tizen мне было задано много вопросов о проценте ложных срабатываний и о плотности ошибок (сколько ошибок PVS-Studio выявляет на 1000 строк кода)…

Комментарии (0)

Следующие комментарии

На сайте установлена reCAPTCHA и применяются
Политика конфиденциальности и Условия использования Google.
Этот сайт использует куки и другие технологии, чтобы предоставить вам более персонализированный опыт. Продолжая просмотр страниц нашего веб-сайта, вы принимаете условия использования этих файлов. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, покиньте данный сайт. Подробнее →
Принять