Новости и разборы
Уязвимость в бизнес-логике: сканер её не видит, а бронь и корзину ломает
GitLab, Cloudflare и Shopify написали о дырах, которые не ловят сканеры: чужой заказ по ID, роль из JSON, цена с клиента. Как я закрываю это в бронировании и заказах.
Самые дорогие дыры в сайте бронирования или в системе заказов выглядят как правильный код. Сервер отдаёт заказ по номеру и не спрашивает, чей он. Поле «роль» в профиле принимает то, что прислал клиент. Сумму считает браузер, а сервер верит. Сканер безопасности здесь молчит, потому что искать ему нечего: проверки нет, а отсутствие кода на шаблон не ложится. На этой неделе об этом написали GitLab, Cloudflare и Shopify, а мне есть что добавить из проката и заказов кофе.
Что вышло
GitLab 20 октября представила Business Logic Security Analyzer, пока в раннем доступе для клиентов Ultimate на GitLab.com. Отправная точка у них верная: нарушение авторизации на уровне объекта стоит первым в OWASP API Security Top 10, а обычный SAST такие вещи не находит. Три примера из текста: эндпоинт берёт ID заказа из запроса и возвращает заказ без проверки владельца; поле роли принимает любой JSON; ID пользователя это последовательное целое число. По отдельности это валидный код, вместе это все заказы всех клиентов и права администратора. Анализатор читает репозиторий целиком и проверяет каждый обработчик на модель того, кто что может. В их демо он находит refund_order, который ставит refunded=1 без проверки владельца, и помечает это как CWE-639.
Cloudflare 21 октября добавила в WAF проверку всей транзакции: запрос смотрят вместе с ответом, и правило может заблокировать ответ сервера до того, как он дойдёт до запросившего. Пример из текста: запрос к файлу ../../config/database.ini сам по себе ещё ничего не доказывает, а ответ 200 с логином и паролем от базы доказывает всё. Дали это всем, включая бесплатный тариф. Одна оговорка оттуда же: заблокировать ответ можно, отменить то, что сервер уже сделал, нельзя. Возврат денег по чужому заказу к этому моменту уже прошёл.
Shopify 20 октября попросила всех разработчиков приложений заняться безопасностью на этой неделе: прогнать новую проверку, перейти на истекающие токены доступа, которые скоро станут обязательными для публичных приложений, посмотреть логи запросов по IP клиента и включить двухфакторную защиту везде. Сама проверка вышла 15 октября как команда shopify app security check в Shopify CLI: она запускается локально, смотрит код, конфигурацию и зависимости, а партнёры, которые её пробовали, нашли у себя пробелы в аутентификации и в изоляции данных между магазинами. Код инструмента открыт.
И новость с другой стороны прилавка. CNews 20 октября передал предупреждение «Лаборатории Касперского»: через уязвимости беспроводных сканеров штрихкодов можно получить доступ к компьютерам склада, а дальше к обработке и отгрузке заказов; такой же путь есть через терминалы сбора данных и термопринтеры.
Как это выглядит в прокате
Возьмём сайт проката автомобилей. Клиент бронирует машину, получает ссылку на договор с номером 1041 и из любопытства открывает 1040. Если сервер отдаёт договор по номеру и не сверяет, чей он, клиент читает паспортные данные соседа. Дальше он замечает, что в запросе на оплату идёт поле price, и меняет его. Если сумму считает браузер, а сервер сохраняет, что пришло, машина уезжает за полцены. Ни одна из этих дыр не выглядит как ошибка в коде.
Вывод: правила о том, кому что можно и как считается сумма, должны жить на сервере и исполняться там. Клиент только показывает результат.
Как это сделано у меня
На сайте проката в Кейптауне, который я довёл до полного цикла бронирования за 2025 год, бронь, документы, подписи и осмотр связаны в одну цепочку. Платёж проверяется по скриншоту, который прикладывают в процессе; это проверка по изображению, а не сверка с банком, и я об этом пишу прямо.
В сервисе «Кофе вместе» правила жёстче, потому что речь о деньгах сотрудников. Gateway на Node.js удаляет присланные клиентом заголовки идентичности и сам определяет пользователя по подписанной HttpOnly cookie, так что «я админ» в запросе ничего не значит. В форме заказа нет цен: сумма появляется только после того, как организатор сверит настоящую корзину поставщика. Оплаченным заказ становится только после того, как организатор сверит поступление денег, а отметку «Заказан» сервис разрешит поставить только после проверки всех оплат и реального размещения заказа у поставщика. Выдать можно только полностью полученный заказ. Каждый переход это проверка на сервере, и именно такие проверки, когда их нет, не видит сканер.
Что я бы сделал
Выписать все точки входа: страницы, API, вебхуки, импорт из таблиц, сканеры на складе. Для каждой ответить, кто имеет право и к каким записям.
Проверить, что сервер сам сверяет владельца у каждого объекта, который отдаёт по ID. Второй аккаунт и чужой номер заказа в адресной строке находят это за пять минут.
Сумму, скидку, статус и роль считать и менять только на сервере. Поля, которые пришли от клиента, принимать по белому списку.
Описать статусы заказа и переходы между ними как правила: из какого состояния в какое можно и после какой проверки.
Смотреть логи по IP и по аккаунту, как советует Shopify.
О проверках, статусах и правах: статья Бизнес-логика: правила, статусы и права. По направлениям: клиентский продукт, связанный с бизнесом и API-интеграции с понятными правилами передачи данных. Системы из этого текста описаны в кейсах проката в ЮАР и «Кофе вместе». Из близких обзоров: вход в кабинет клиента: passkeys, SSO и ссылка без пароля, магазины готовят витрины для AI-агентов и zero trust для компании из 30 человек; раньше я писал про корзину перед оплатой и безопасность вебхуков. Если в вашей системе заказов есть ID в адресной строке и сумма, которую присылает клиент, расскажите, как она устроена, и я скажу, что проверить первым. Обсудить задачу.