Как стать автором
Обновить
0
ГК ITGLOBAL.COM
Рассказываем про Managed IT, облака и ИБ.

Почему стоит проверять приложения на устойчивость к race condition

Время на прочтение4 мин
Количество просмотров4.6K
Если ваше приложение или сервис работает с внутренней валютой, следует проверить его на уязвимости типа race condition («состояние гонки» или, если точнее, — «неопределенность параллелизма»). Race condition — это «плавающая ошибка», которую могут эксплуатировать злоумышленники. Суть в том, что благодаря параллельному выполнению кода можно получить доступ к внутренней валюте приложения, манипулировать ею и, при желании, нанести ощутимый финансовый ущерб владельцу сервиса. Недавно мы обнаружили такую проблему у одного из наших клиентов и помогли ее решить.



Что такое race condition


Так как разработчики часто забывают, что код может выполняться несколькими потоками одновременно, они не тестируют продукт на уязвимость типа race condition, хотя эта ошибка довольно распространена.

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

Вот конкретный пример такой уязвимости. Допустим, у нас есть приложение, которое позволяет переводить бонусы между платежными кошельками. У злоумышленника есть два кошелька — A и B, и на каждом из них есть 1000 бонусов. На схеме показано, как манипулируя временем отправки запроса на транзакцию, злоумышленник может увеличивать сумму перевода на свой счет и сделать из 10 бонусов — 20.



Существуют автоматические инструменты для поиска таких уязвимостей. Например, RacePWN который за минимальное время отправляет множество HTTP-запросов на сервер и принимает на вход json-конфигурацию, облегчая процесс атаки для злоумышленников. Вручную же это делается с помощью отправки POST-запросов.

Смертельная race condition


В США с июня 1985 года по январь 1987 года ошибка race condition в аппарате лучевой терапии Therac-25, созданном канадской государственной организацией Atomic Energy of Canada Limited (AECL) стала причиной шести передозировок радиацией. Жертвы получили дозы в десятки тысяч рад. Смертельным считается уровень в 1000 рад. После полученных ожогов пострадавшие умерли в течение нескольких недель. Выжить удалось только одной пациентке.

Предыдущие модели Therac имели аппаратные механизмы защиты: независимые цепи блокировки, контролирующие электронный луч; механические блокираторы; аппаратные автоматические выключатели; отключающие предохранители. В Therac-25 аппаратную защиту убрали. За безопасность отвечало программное обеспечение. Аппарат имел несколько режимов работы, и из-за ошибки race condition врач иногда не понимал, в каком режиме аппарат работает на самом деле. В ходе судебных разбирательств выяснилось, что ПО Therac-25 было разработано одним программистом, но у AECL не было данных, кем конкретно.

По итогам процесса правительство США серьезно ужесточило требования к проектированию и работе систем, чья безопасность критична для людей.

Как защититься


Проще и дешевле всего решить проблему race condition — правильно спроектировать архитектуру приложения. Вот что для этого следует предусмотреть.

  • Блокировку критически важных записей в базе данных. Есть разные способы обеспечить работу с записью одного потока в конкретный момент времени. Главное — не заблокировать ничего лишнего.
  • Изоляцию транзакций в базе данных, которая гарантирует, что транзакции будут совершены последовательно. Самое важное здесь — соблюсти баланс между безопасностью и скоростью.
  • Использование мьютекса. Эта функция защищает конкретный участок кода от одновременного доступа к нему нескольких потоков. Здесь также следует соблюсти баланс, иначе можно создать новые проблемы, например, взаимную блокировку кода или его двойной захват. Кстати, в кейсе, который мы сейчас расскажем, мы использовали именно этот способ защиты.

Как мы нашли уязвимость


Наш клиент — интернет-магазин доставки продуктов, который поддерживает функцию предоставления скидок с помощью купонов. В процессе тестирования мы обнаружили уязвимость — при отправке POST-запроса со значением купона. Отправляя запрос с различными временными задержками, удавалось получить скидку дважды. Судя по всему, разработчики допустили грубую ошибку, связанную с разделяемым доступом к объекту, который отождествлялся с покупкой.

Скорее всего имел место такой псевдокод без механизмов синхронизации:

1 If promo_flag is not set:
2 Price = get_price()
3 Price -= price * promo_percent;
4 set_price(price)
5 set_promo_flag()

Здесь, применение промокода и установка соответствующего флага не являются атомарной операцией. Вероятней всего, когда началось второе применение промокода, первое остановилось на 5-й строке (то есть еще не выполнилось). В этот момент функция get_price() во второй строке вернула уже новое значение цены, уже со скидкой.

Решение


Проблема решается просто:

1 acqure_mutex()
2 If promo_flag is not set:
3 Price = get_price()
4 Price -= price * promo_percent;
5 set_price(price)
6 set_promo_flag()
7 release_mutex()

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

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



Блог ITGLOBAL.COM — Managed IT, частные облака, IaaS, услуги ИБ для бизнеса:

Теги:
Хабы:
Всего голосов 3: ↑1 и ↓2-1
Комментарии7

Публикации

Информация

Сайт
itglobal.com
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия
Представитель
itglobalcom

Истории