Веб
Безопасность API: практическое руководство по OWASP API Security Top 10
API за мобильным приложением и SPA на экспертизе проверяют отдельно. Каждый пункт OWASP API Top 10 (2023), как проверить его самостоятельно и как закрыть в Laravel и Node.
· 11 мин. чтения

Современный сайт редко бывает «просто фронтендом». React- или Vue-приложение в браузере, мобильное приложение, системы партнёров — все общаются с одним API. Пользователь видит в интерфейсе только разрешённые ему кнопки, но для прямого запроса к API кнопки не нужны: хватит curl или прокси-инструмента. Поэтому безопасность API должна опираться не на ограничения интерфейса, а на проверки на сервере.
У OWASP для этой области есть отдельный список — OWASP API Security Top 10 (2023). В этой статье мы коротко объясняем каждый пункт, показываем, как проверить его в своей системе и как исправить на примере Laravel и Node (Express).
Почему это важно для экспертизы
На официальной экспертизе по кибербезопасности специалисты проверяют приложение не только через браузер, но и на уровне API: перехватывают запросы, подменяют идентификаторы, добавляют лишние поля, повторяют запросы без токена или с токеном другого пользователя. На экспертизе мобильного приложения из него извлекают адреса API и проверяют бэкенд отдельно. То есть ошибка в API может попасть и в веб-отчёт, и в мобильный.
На экспертизе нашего собственного продукта — SaaS-платформы для налогов и финансов — часть находок относилась именно к этому классу: действия, которые можно было выполнить без нужной проверки состояния (ошибка бизнес-логики), и лишние данные в ответах (раскрытие информации).
API1: Broken Object Level Authorization (BOLA)
Первая и самая распространённая проблема. API возвращает объект по идентификатору, но не проверяет, принадлежит ли он пользователю, отправившему запрос. В веб-приложениях это известно как IDOR — подробно мы писали об этом в отдельной статье.
Проверка: заведите два тестовых аккаунта (A и B). Получите объект аккаунтом A, затем запросите тот же ID с токеном B:
curl -s https://api.your-domain.uz/v1/invoices/1042 \
-H "Authorization: Bearer <B_TOKEN>"
Ответ должен быть 403 или 404, а не данные A. Проверяйте не только GET, но и PUT, PATCH, DELETE и эндпоинты скачивания файлов.
Исправление в Laravel — policy для каждого объекта или ограничение запроса по владельцу:
public function show(Invoice $invoice)
{
$this->authorize('view', $invoice);
return new InvoiceResource($invoice);
}
// или ограничить сам запрос
$invoice = $request->user()->invoices()->findOrFail($id);
В Node/Express тот же принцип: ищите не по ID, а по «ID + владелец».
const invoice = await db.invoice.findFirst({
where: { id: req.params.id, ownerId: req.user.id },
});
if (!invoice) return res.sendStatus(404);
API2: Broken Authentication
Ошибки механизма аутентификации: неограниченный перебор паролей, бессрочные токены, слабо подписанные токены, недостатки в восстановлении пароля. О защите входа мы писали здесь, об ошибках с токенами — в статье про JWT.
Вопросы для проверки:
- Ограничено ли число попыток на
/login,/password/reset,/otp/verify? - Есть ли у токена срок жизни и действительно ли он аннулируется после выхода?
- Нет ли в мобильном API незащищённого пути входа, оставленного «для старой версии»?
API3: Broken Object Property Level Authorization
Этот пункт объединяет две старые проблемы: избыточный возврат данных и mass assignment. В первом случае API возвращает весь объект модели, а нужные поля выбирает фронтенд — при этом в ответе остаются хеш пароля, внутренние заметки или телефоны других пользователей. Во втором API записывает пришедший JSON прямо в модель, и пользователь может добавить "role": "admin" или "balance": 999999.
Проверка: посмотрите ответы в DevTools или в прокси — есть ли поля, которых не видно в интерфейсе? Добавьте лишнее поле в запрос обновления профиля:
curl -s -X PATCH https://api.your-domain.uz/v1/profile \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"name":"Test","is_admin":true,"role":"admin"}'
Затем прочитайте профиль снова: изменилась ли роль?
Исправление в Laravel — API Resource для ответа и провалидированные данные на входе:
// записываются только разрешённые поля
$user->update($request->validated());
class UserResource extends JsonResource
{
public function toArray($request): array
{
return ['id' => $this->id, 'name' => $this->name];
}
}
Не используйте в модели $guarded = [], явно перечисляйте $fillable. В Node тоже не передавайте req.body напрямую в update(): выделяйте нужные поля схемой (zod, joi).
API4: Unrestricted Resource Consumption
Не ограничено, сколько ресурсов API тратит на один запрос: списки без пагинации, ?limit=1000000, загрузка огромных файлов, неограниченная отправка SMS или писем. В итоге сервер тормозит, а счета за платные сервисы (SMS-шлюз) растут.
В Laravel — middleware throttle и предел пагинации:
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
$perPage = min((int) $request->query('per_page', 20), 100);
В Express — пакет express-rate-limit и ограничение размера тела, например express.json({ limit: '100kb' }). На уровне веб-сервера можно задать client_max_body_size и limit_req — это показано в статье про nginx.
API5: Broken Function Level Authorization
Если BOLA касается объекта, то этот пункт — функции: обычный пользователь может вызвать админский эндпоинт. Часто админские пути только скрыты в интерфейсе, а на сервере роль не проверяется.
Проверка: поищите в JavaScript-файлах фронтенда или в мобильном приложении пути вроде /admin, /internal, /manage и вызовите их с токеном обычного пользователя. Там, где разрешён GET, попробуйте и DELETE.
В Laravel сгруппируйте админские маршруты и защитите их middleware или Gate:
Route::middleware(['auth:sanctum', 'can:admin'])
->prefix('admin')
->group(function () { /* ... */ });
API6: Unrestricted Access to Sensitive Business Flows
Технически всё корректно, но бизнес-процесс можно автоматизировать и злоупотребить им: массово собирать промокоды, скупать ограниченный товар ботами, автоматизировать регистрацию. Решение зависит от контекста: лимиты на пользователя и устройство, серверная проверка последовательности действий, мониторинг подозрительной активности.
Близкая проблема встретилась и на нашей экспертизе: страница «успешной оплаты» показывалась до того, как платёжный провайдер подтвердил платёж. Об этом есть отдельная статья. Главное правило: принимайте смену состояния (оплачено, подтверждено, отправлено) только после проверки на сервере.
API7: Server Side Request Forgery (SSRF)
Если API отправляет запрос по URL от пользователя (импорт картинки, webhook, превью ссылки), атакующий может через сервер обратиться во внутреннюю сеть или к сервису метаданных облака. Защита: список разрешённых доменов, блокировка внутренних диапазонов IP, ограничение редиректов.
API8: Security Misconfiguration
Неверные настройки: режим отладки в production, ошибки со stack trace, лишние HTTP-методы, отсутствие заголовков безопасности, неправильный CORS. Обо всём этом у нас есть отдельные статьи: режим отладки, заголовки безопасности, CORS.
Быстрая проверка — запрос к несуществующему эндпоинту и с некорректным JSON:
curl -s https://api.your-domain.uz/v1/nonexistent
curl -s -X POST https://api.your-domain.uz/v1/login \
-H "Content-Type: application/json" -d '{bad json'
В ответе не должно быть путей к файлам, SQL-запросов или версии фреймворка.
API9: Improper Inventory Management
Знаете ли вы, какие API у вас работают? Часто v1 обновляют, а v0 оставляют для старых мобильных приложений и забывают. Или поддомен staging.api открыт с боевыми данными. Экспертиза может найти такие «забытые» двери.
В нашем опыте была близкая находка: в браузер отдавался полный список маршрутов приложения. Это даёт атакующему готовую карту API. Если в Laravel вы используете Ziggy, выводите только нужные маршруты:
// config/ziggy.php
return [
'only' => ['dashboard', 'invoices.*', 'profile.*'],
];
Практические шаги: составьте список всех версий и хостов API, отключите неиспользуемые, не оставляйте Swagger/OpenAPI открытым в production или закройте его аутентификацией.
API10: Unsafe Consumption of APIs
Ваш API получает данные от других API (платёжная система, госсервисы, карты). Если слепо доверять внешнему ответу — записывать в базу или выводить на странице без проверки, — проблема внешней системы становится вашей. Валидируйте и внешние ответы, подключайтесь только по HTTPS, задавайте таймаут и не следуйте редиректам автоматически.
В HTTP-клиенте Laravel:
$response = Http::timeout(10)
->withOptions(['allow_redirects' => false])
->get('https://partner.example/api/status');
$data = validator($response->json(), [
'status' => 'required|in:paid,pending,failed',
])->validate();
Короткий план самопроверки
- Составьте список всех эндпоинтов API (
php artisan route:list --path=apiили роутеры Express). - Подготовьте два тестовых аккаунта и один админский.
- Вызовите каждый эндпоинт без токена, с токеном другого пользователя и админские функции — с токеном обычного пользователя.
- Отправьте лишние поля в эндпоинты записи.
- Сравните ответы с тем, что видно в интерфейсе.
- Проверьте ограничение частоты на входе, OTP и SMS.
- Запишите результаты в таблицу и после исправления каждой находки проверьте снова.
Делайте это только в своей системе и на тестовых данных.
Граница безопасности в API — это сервер. Кнопка, скрытая в интерфейсе, защитой не является.
Чек-лист
- В каждом эндпоинте объекта есть проверка владения (policy или ограничение запроса).
- Админские и внутренние функции защищены на сервере по роли.
- Ответы отдаются через API Resource или DTO, только с нужными полями.
- В запросах на запись принимаются только провалидированные поля.
- На входе, OTP, SMS и тяжёлых эндпоинтах есть ограничение частоты и предел пагинации.
- Отладка выключена, ошибки возвращаются общим сообщением.
- Старые версии API, тестовые хосты и открытая документация инвентаризированы.
- Ответы внешних API валидируются, задан таймаут.
Итог
OWASP API Top 10 — не теория, а список конкретных вопросов, которые проверяют на экспертизе. Большинство из них сводится к одному правилу: на каждый запрос сервер должен ответить на вопрос «вправе ли этот пользователь выполнить это действие с этим объектом и в таком объёме?».
Хотите проверить свой API по этому списку до экспертизы? Оставьте заявку на сканирование — Claude AI агент быстро просмотрит эндпоинты и код, а наш специалист подтвердит находки и поможет закрыть дыры.


