Race Condition: Double-spend баланса
Race condition: чтение и списание баланса без транзакции позволяет двойную трату при параллельных запросах.
mediumgolangPro
Задача
# Race Condition: TOCTOU в операциях с балансом (double spend)
## Сценарий
Вы — аудитор интернет-магазина **SecureShop**. У каждого зарегистрированного пользователя есть баланс, с которого списываются средства при покупке товаров. Команда уверена, что эндпоинт покупки безопасен — серверный код проверяет баланс перед списанием, и пользователь не может потратить больше, чем у него есть. По данным внутреннего аудита, в каталоге магазина есть служебный товар «CTF Flag» — он не должен быть приобретён обычным пользователем, потому что баланс жертвы заведомо меньше его цены.
## Цель
1. Исследуйте серверную логику обработчика покупки — как именно сервер проверяет достаточность средств и списывает их.
2. Найдите способ обойти проверку баланса, не прибегая к классическим уязвимостям (без SQL-инъекций, без подмены параметров запроса) — используя свойства параллельной обработки запросов сервером.
3. Приобретите служебный товар «CTF Flag» и получите флаг из ответа.
## Теория
TOCTOU (Time-of-Check Time-of-Use) — класс уязвимостей, при которых между моментом проверки условия и моментом фактического использования его результата существует временное окно, в которое состояние может измениться. В контексте веб-приложений это часто выглядит как последовательность «прочитать → проверить → записать», выполняемая на уровне приложения **без атомарной транзакции** и **без блокировки строки** в БД.
**Уязвимый паттерн:**
```go
// Read-Check-Update без транзакции — race condition
balance := readBalance(userID) // 1. read
if balance >= price { // 2. check
updateBalance(userID, balance-price) // 3. update — между шагами могут пройти другие запросы
insertOrder(userID, productID)
}
```
При параллельной отправке нескольких запросов на покупку сервер обрабатывает их в нескольких goroutine одновременно. Если все они успевают выполнить шаг 1 **до того**, как любой из них завершит шаг 3, все увидят одинаковый баланс, все пройдут проверку (шаг 2), и все произведут списание. Итог: за один реальный баланс пользователь получает несколько товаров — баланс уходит в минус, заказы дублируются.
## Точка входа атаки
| Параметр | Значение |
|----------|----------|
| Учётные данные | `demo` / `demo` |
| Эндпоинт покупки | `POST /api/buy` (параметр `product_id`) |
| Эндпоинт проверки баланса | `GET /api/balance` |
| Цель | приобрести «CTF Flag» через параллельные запросы и получить флаг из ответа |