Business Logic Flaws: баги, которые сканер никогда не найдёт
«Твой Burp Scanner нашёл 0 уязвимостей? Поздравляю, приложение технически безопасно и полностью сломано одновременно.» 💀
Бизнес-логика — это единственный класс уязвимостей, где код работает абсолютно правильно, но правила, которые он реализует, дырявые как решето. Sqlmap не найдёт баг в checkout-флоу, который пропускает проверку оплаты. Nuclei не поймает race condition в купонах. Это работа только для человека с мозгами и пониманием контекста.
🧠 Почему сканеры здесь бессильны
Сканеры ищут сигнатуры — известные паттерны инъекций, XSS-маркеры, дефолтные креды. Бизнес-логика не оставляет сигнатур, потому что запрос технически валиден.
Сканер видит:
POST /api/checkout {"item_id": 42, "coupon": "SAVE10"}
→ 200 OK, валидный JSON, никаких инъекций
→ "Всё чисто!" ✅
Человек видит:
→ А что если отправить купон 100 раз параллельно?
→ А что если пропустить шаг оплаты и перейти прямо на confirm?
→ А что если id товара отрицательный?
🔥 Главный принцип охоты за логическими багами: «Am I allowed to do this?» — спрашивай себя это на КАЖДОМ шаге. Не ищи баг в коде, ищи баг в предположениях разработчика.
📋 OWASP Business Logic Abuse Top 10 (2025) — новый стандарт
OWASP выкатил отдельный список именно для бизнес-логики. Это не замена основному Top 10, это дополнение, потому что логические баги — отдельная категория боли.
| # | Класс | Суть |
|---|---|---|
| BLA1 | Action Limit Overrun | Race condition обходит одноразовые операции (купоны, рефанды) |
| BLA2 | Workflow Order Bypass | Финальный шаг флоу выполняется до предыдущих |
| BLA3 | Object State Manipulation | Незащищённые поля объекта переопределяют роли/права |
| BLA4 | Malicious Logic Loop | Отсутствие лимитов на циклы/рекурсию → DoS |
| BLA5 | Artifact Lifetime Exploitation | Токены/сессии живут дольше положенного |
| BLA6 | Missing Transition Validation | Пропуск проверок в многошаговом workflow |
| BLA7 | Resource Quota Violation | Нет rate-limit на дорогие операции |
| BLA8 | Internal State Disclosure | Разные ответы для valid/invalid раскрывают внутреннюю логику |
| BLA9 | Broken Access Control | Спуфинг роли через логические дыры |
| BLA10 | Shadow Function Abuse | Незащищённые internal API/test-утилиты в проде |
💰 BLA1: Race Condition — многократное использование одноразового
Классика бизнес-логики. Купон на «одно использование» на самом деле можно применить N раз, если отправить N параллельных запросов быстрее, чем сервер успеет обновить статус.
# Атака Race Condition через параллельные запросы
for i in {1..20}; do
curl -X POST https://target.com/api/apply-coupon \
-H "Authorization: Bearer $TOKEN" \
-d '{"coupon":"SAVE50"}' &
done
wait
# Или через Burp Suite — Turbo Intruder с race condition template
# https://github.com/PortSwigger/turbo-intruder
💡 Лайфхак: ищи любую операцию с формулировкой «только один раз» — reset пароля, применение купона, голосование, лайк. Везде, где есть проверка if not used: use(), есть TOCTOU-окно (time-of-check to time-of-use).
Реальный пример: известны случаи, когда исследователи получали скидку 90%+ на товар, отправляя купон параллельно 50 раз — сервер применял его N раз до того, как флаг «использован» успевал обновиться.
🔀 BLA2: Пропуск шагов в workflow
Многошаговые процессы (checkout, KYC, onboarding) часто проверяют состояние только на клиенте, а не на сервере. Если можно вызвать шаг 5, не проходя шаги 1–4 — добро пожаловать в баг.
# Нормальный флоу checkout:
POST /api/cart/add → POST /api/cart/verify → POST /api/payment → POST /api/confirm
# Атака — прыгаем прямо к confirm, минуя оплату
POST /api/confirm
{"order_id": 12345}
# Если сервер не проверяет, был ли реально совершён платёж —
# товар твой бесплатно 🎯
# Тестирование пропуска шагов через Burp Repeater
# 1. Пройди нормальный флоу, запиши все запросы
# 2. Начни новую сессию
# 3. Вызывай финальные эндпоинты, пропуская промежуточные
# 4. Смотри, проверяет ли сервер prerequisite state
🎭 BLA3: Object State Manipulation — Mass Assignment 2.0
Если API привязывает пользовательский JSON прямо к внутреннему объекту без фильтрации полей — ты можешь переопределить то, что не должен видеть.
// Обычный запрос обновления профиля
POST /api/user/update
{"name": "John", "email": "john@test.com"}
// Атака — добавляем поле, которого "не должно быть" в форме
POST /api/user/update
{"name": "John", "email": "john@test.com", "role": "admin", "is_verified": true, "balance": 999999}
🧠 Где искать: любой PUT/PATCH эндпоинт с ORM (Django, Rails, Sequelize) без явного whitelist полей. Проверь через Burp — сохрани обычный запрос, добавь по одному потенциально «скрытому» полю (role, admin, verified, balance, discount) и смотри на реакцию сервера.
⏳ BLA5: Artifact Lifetime — токены живут вечно
Password reset токен должен умирать после первого использования или через 15 минут. На практике — сплошь и рядом живёт часами, а то и не инвалидируется вообще.
# Тестируем lifetime токена сброса пароля
1. Запроси сброс пароля → получи токен в письме
2. Используй токен → смени пароль
3. Попробуй использовать ТОТ ЖЕ токен снова
curl -X POST https://target.com/reset-password \
-d '{"token":"OLD_TOKEN", "new_password":"hacked123"}'
4. Если сработало — токен не инвалидируется после использования 💀
🔓 BLA9: Broken Access Control через логику, не через код
Отличается от классического IDOR тем, что здесь все проверки прав технически на месте, но их порядок или условие сформулированы неправильно.
Пример:
→ API проверяет "является ли пользователь владельцем заказа"
→ НО не проверяет "является ли заказ активным/принадлежит текущей организации"
→ Результат: можно менять чужие заказы в других тенантах (multi-tenancy leak)
# Найден в реальном пентесте: смена статуса заказа другой компании
PATCH /api/orgs/456/orders/789/status
Authorization: Bearer <token_org_123>
{"status": "cancelled"}
# Сервер проверяет только "существует ли заказ 789"
# Не проверяет "принадлежит ли org 456 текущему пользователю из org 123"
👻 BLA10: Shadow Function Abuse — забытые внутренние функции
Разработчики оставляют «временные» эндпоинты для тестирования, debug-инструменты, internal API — и забывают их удалить перед продом.
# Поиск shadow-функций через JS файлы (см. статью про разведку)
cat wayback.txt | grep -E "internal|debug|test|admin_only|_temp"
# Типичные находки:
/api/internal/reset-all-users
/api/debug/impersonate?user_id=1
/api/test/create-admin
/api/v1/_deprecated/bypass-payment
💣 Лайфхак: ищи различия между тем, что видно во фронтенде, и тем, что доступно через API. Frontend роутинг ≠ backend авторизация. Если фронт скрывает кнопку — это не значит, что эндпоинт защищён.
🛠️ Методология охоты за логическими багами
Три правила от практикующих пентестеров:
- Изучи бизнес-домен глубоко — как пользователи реально взаимодействуют, какие права нужны для каждого действия
- Постоянно спрашивай «а что если…?» — не ставь себе искусственных лимитов при тестировании
- Действуй, будто препятствий не существует — ошибка 403 это не стена, это флаг, что механизм защиты можно обойти
Практический чеклист:
□ Многошаговые процессы: пробуй пропускать шаги, менять порядок
□ Одноразовые операции: атакуй параллельными запросами (race condition)
□ Формы с "скрытыми" полями: добавляй role/admin/balance/discount
□ Токены/сессии: проверяй lifetime, инвалидацию после использования
□ Multi-tenancy: проверяй изоляцию между организациями/аккаунтами
□ Отрицательные/нулевые значения: цена -1, количество 0, скидка 999%
□ Параллельные вызовы одной операции: 2 раза оплатить один заказ
□ JS-файлы и wayback: ищи shadow/internal/debug эндпоинты
# Быстрый тест на отрицательные значения в ценообразовании
curl -X POST https://target.com/api/cart/add \
-d '{"item_id": 1, "quantity": -5, "price": -100}'
# Если сервер вычтет отрицательное количество из общей суммы —
# ты можешь получить отрицательный total = сервер тебе должен денег 🎯
🏆 Как защититься (если ты по другую сторону)
- Валидируй всё на сервере, никогда не доверяй client-side проверкам состояния
- Явно документируй все предположения о поведении пользователя в дизайн-документах — то, что не задокументировано, будет сломано
- Whitelist полей в API, никогда не bind напрямую JSON к внутренним моделям
- Атомарные операции с блокировками для всего, что должно случиться «только один раз»
- Регулярный manual pentest — automated scanning тут просто не работает
Итог: Business logic flaws — это баги в предположениях, а не в синтаксисе. Пока разработчики думают «пользователь не будет делать так» — ты делаешь именно так. Сканер ищет паттерны, ты ищешь дыры в здравом смысле дизайнера системы. 😈
Похожие статьи

GraphQL: интроспекция включена, безопасность — нет

BOLA/IDOR в REST API: итерируй всё подряд и молчи
