Мы проверяли боты и Mini Apps на аудитах и на собственных пилотах. Порядок в списке — по частоте находок. Самое частое здесь заодно и самое скучное.
1. Подпись initData не проверяется
Mini App получает данные о пользователе строкой initData, подписанной токеном
бота. Проверка занимает пятнадцать строк кода. Пропускают её примерно в половине
случаев — потому что в разработке всё работает и без неё.
Последствие: любой человек с curl оформляет заказ, списывает бонусы или читает
историю от имени произвольного user_id. Никакого взлома, просто HTTP-запрос.
Проверять нужно на сервере и при каждом запросе. Одной проверки при открытии
приложения не хватает. И отдельно смотрите на auth_date: подписанная строка
недельной давности — валидная подпись и невалидная сессия.
2. Прямой доступ к чужим объектам
Классика OWASP под номером один, в ботах — в почти неизменном виде. Эндпоинт
вида /api/orders/1042 отдаёт заказ по идентификатору, а проверки «этот заказ
принадлежит этому пользователю» нет.
Идентификаторы последовательные, поэтому выгрузить всю базу заказов — это цикл на десять строк. Мы находили так адреса доставки, номера телефонов и суммы.
Обфускация не спасает, UUID тут почти не помогает. Лечится проверкой
владения в слое доступа к данным: запрос всегда содержит where user_id = ...,
и это не забывается, потому что иначе не компилируется.
3. Токен бота и ключи эквайринга в репозитории
.env в гите, ключи в docker-compose, токен в исходнике «на время отладки».
Приватный репозиторий не защита: подрядчики меняются, форки остаются, история
не удаляется вместе с файлом.
Токен бота позволяет читать все сообщения и писать от имени бота всем, кто с ним общался. Ключ эквайринга — инициировать возвраты.
Что делать: секреты только в переменных окружения, .env.example без значений в
репозитории, сканер секретов в CI. Если токен когда-либо попадал в историю, его
отзывают. Удалить файл недостаточно.
4. Отсутствие ограничения частоты запросов
Бот принимает заявки, каждая заявка отправляет уведомление в Telegram и пишет строку в базу. Ограничений нет. Один скрипт создаёт десять тысяч заявок за минуту, менеджер получает десять тысяч уведомлений, база растёт, реальные заявки теряются в шуме.
Данные при этом не утекают, но бизнес-процесс встаёт, и разгребать дольше, чем атаковать.
Минимум: ограничение по идентификатору пользователя и по IP, honeypot-поле в формах, проверка минимального времени заполнения. Капча — крайняя мера, она стоит конверсии.
5. Вебхук платежей без проверки подписи
Провайдер присылает уведомление об успешной оплате. Обработчик доверяет телу запроса и переводит заказ в оплаченные. Адрес вебхука можно узнать из фронтенда или подобрать — он редко бывает секретным.
Дальше любой желающий отправляет POST и получает товар бесплатно.
Проверять нужно подпись уведомления и отдельно запрашивать статус платежа у провайдера по его API, прежде чем что-то отгружать. Уведомление — это сигнал «сходи проверь».
Чего в этом списке нет
Нет экзотики: цепочек из трёх уязвимостей, гонок состояний, атак на цепочку поставок. Они существуют, мы их иногда находим, но они не про восемь из десяти ботов.
Восемь из десяти ломаются на вещах из списка выше, и все пять закрываются примерно за день работы. На старте это день. После инцидента — неделя и разговор с клиентами.
Как проверить свой бот за час
- Отправьте запрос к API бота без
initData— ответит ли он. - Возьмите свой идентификатор заказа, вычтите единицу, запросите — что придёт.
- Поищите в истории репозитория строки
bot,token,secret,key. - Отправьте сто запросов подряд — сработает ли что-нибудь.
- Отправьте платёжный вебхук руками с валидным телом и неверной подписью.
Если хотя бы один пункт прошёл, уязвимость уже есть. И вы вряд ли первый, кто её нашёл.