Щоб зупинити застосунок, не потрібні мільйони запитів: досить кількох, кожен з яких змушує сервер робити величезну роботу. Захист від мережевих атак (CDN, WAF) тут не допомагає - запити виглядають цілком легітимно.
Типові «дорогі» запити:
| Вектор | Приклад |
|---|---|
| необмежена пагінація | GET /api/orders?per_page=1000000 |
| важкі фільтри й сортування | сортування за колонкою без індексу, LIKE '%...%' по мільйонах рядків |
| великі тіла запитів | JSON на сотні мегабайтів, масив з мільйоном елементів |
| файли | гігабайтні завантаження, zip-бомба, «бомба декомпресії» в зображенні (крихітний PNG, що розпаковується в гігабайти пікселів) |
| регулярні вирази | катастрофічний бектрекінг на рядку від користувача (ReDoS) |
| дорогі операції без ліміту | генерація PDF, експорт, виклик LLM чи платного API на кожен запит |
| хешування | дуже довгі паролі з дорогим алгоритмом хешування |
Захист у Laravel:
// пагінація: верхня межа
$perPage = min($request->integer('per_page', 20), 100);
// валідація розмірів і кількостей
$request->validate([
'items' => ['required', 'array', 'max:100'],
'items.*.sku' => ['required', 'string', 'max:64'],
'avatar' => ['image', 'max:2048', 'dimensions:max_width=4000,max_height=4000'],
'password' => ['required', 'string', 'max:128'],
]);
- сортування й фільтри лише зі списку дозволених колонок (які мають індекси);
- обмеження частоти для дорогих маршрутів окремо:
RateLimiter::for('exports', fn (Request $r) => Limit::perMinute(3)->by($r->user()->id)); - важка робота - у черзі, з окремим пулом воркерів, щоб експорт не забирав процеси вебзапитів;
- тайм-аути:
statement_timeout/max_execution_timeдля бази, тайм-аути HTTP-клієнта до сторонніх сервісів.
Межі на рівні інфраструктури:
client_max_body_sizeу Nginx,post_max_sizeіupload_max_filesizeу PHP - тіло відкидається ще до Laravel;memory_limitіmax_execution_time- щоб один запит не з'їв увесь сервер;- кількість воркерів PHP-FPM - обмежений ресурс: повільні запити займають їх, і решта користувачів чекає в черзі.
Неочевидні місця:
- розпакування архівів - перевіряти сумарний розмір і кількість файлів під час розпакування, а не за розміром архіву;
- зображення - перевіряти розміри в пікселях до обробки, а не лише розмір файлу;
- GraphQL і вкладені
includeв API - обмеження глибини й складності запиту.
Перевірка: у тестах навмисно надсилати граничні значення (per_page=100000, масив на 10 000 елементів) і очікувати 422, а не 500 і не хвилину очікування.
Докладніше в документації: OWASP API4:2023 Unrestricted Resource Consumption