REST API: захист, кешування й ліміти
20 питань · ~20 хв · Версія v3.0
Увійдіть, щоб продовжити
Токени, ключі й OAuth, CORS, HTTP-кешування, обмеження частоти, помилки й вебхуки - питання всіх рівнів, від junior до lead.
- За спробу
- 20
- У пулі
- 46
- Проходжень
- 0
- Середній бал
- -
- Пройшли на 70%+
- -
Питання для підготовки
31 питанняКоли ваш сервіс викликає інший (мікросервіс, партнерський API, внутрішній воркер), потрібно довести, який сервіс робить запит, і що запит не змінено по дорозі.
1. Статичний API-ключ / спільний секрет у заголовку - найпростіше:
Authorization: Bearer svc_billing_8f2a...
Мінуси: довгоживучий секрет, що зберігається в кількох місцях; викрадений ключ працює звідусіль; ротація болісна. Прийнятно для простих інтеграцій із ротацією й обмеженням за IP.
2. Підпис запиту (HMAC) - секрет не передається мережею, передається підпис:
X-Timestamp: 1760000000
X-Signature: hex(HMAC-SHA256(secret, method + path + timestamp + sha256(body)))
Отримувач обчислює підпис сам і порівнює у постійному часі (hash_equals). Мітка часу з коротким вікном (кілька хвилин) захищає від повторного відтворення. Так підписують вебхуки (Stripe, GitHub) і запити AWS (SigV4).
3. Взаємний TLS (mTLS) - обидві сторони пред'являють сертифікати:
- сервер перевіряє клієнтський сертифікат, виданий вашим внутрішнім центром сертифікації;
- ідентичність сервісу - у сертифікаті, а не в секреті в коді;
- RFC 8705 описує прив'язку OAuth-токенів до сертифіката: викрадений токен без приватного ключа клієнта непридатний.
Мінус - інфраструктура: власний CA, видача й ротація сертифікатів. Service mesh (Istio, Linkerd) роблять mTLS прозорим для застосунку.
4. Короткоживучі токени від центрального сервісу ідентифікації - OAuth 2.0 Client Credentials:
POST /oauth/token
grant_type=client_credentials&client_id=billing&client_secret=...&scope=orders:read
Сервіс отримує токен на хвилини з конкретними правами (scopes), отримувач перевіряє підпис і aud. У хмарах - ідентичності робочих навантажень (IAM-ролі, workload identity) без статичних секретів узагалі.
Як обрати:
- дві-три внутрішні інтеграції - підписані запити з ротацією секретів;
- багато сервісів, потрібні права й аудит - OAuth client credentials (у Laravel - Passport);
- мережа з нульовою довірою, високі вимоги - mTLS, часто разом із токенами.
Обов'язкове в будь-якому варіанті: найменші права для кожного сервісу, ротація секретів без простою (два дійсні ключі на час переходу), журнал викликів.
Клієнту API потрібно не лише знати, що пішло не так, а й обробити це програмно. Для цього помилки мають бути в одному передбачуваному форматі на весь API.
Problem Details (RFC 9457, замінив RFC 7807) - стандартний формат з типом application/problem+json:
{
"type": "https://api.example.com/problems/insufficient-funds",
"title": "Недостатньо коштів",
"status": 422,
"detail": "На рахунку 30 грн, а списання - 50 грн.",
"instance": "/payments/8f3a",
"balance": 3000
}
type- URI типу помилки, стабільний ідентифікатор для програмної обробки (бажано - з документацією за цією адресою).title- короткий опис типу, однаковий для всіх його випадків.status- HTTP-код (дублює заголовок для зручності).detail- пояснення саме цього випадку.instance- ідентифікатор конкретного випадку.- Можна додавати власні поля - як
balanceвище чи список помилок валідації за полями.
Принципи, незалежно від формату:
- Правильний HTTP-код - формат тіла його не замінює.
- Машиночитний код помилки (
typeчиcode), а не лише текст: текст змінюють і перекладають, а клієнти на нього не мають покладатися. - Помилки валідації за полями, щоб клієнт підсвітив поля форми.
- Жодних стек-трейсів, SQL і шляхів до файлів у відповіді на проді.
- ID запиту в тілі чи заголовку - щоб знайти помилку в логах за зверненням клієнта.
Докладніше в документації: RFC 9457: Problem Details for HTTP APIs
- Автентифікація відповідає на питання «хто ви?»: перевіряє токен, ключ, пароль.
- Авторизація - «що вам можна?»: чи має цей користувач право на цю дію з цим ресурсом.
Спершу автентифікація, потім авторизація. Помилки - різні: 401 - не вдалося встановити, хто ви; 403 - відомо, хто ви, але дія заборонена.
Способи автентифікації в API:
- API-ключ у заголовку (
X-API-KeyчиAuthorization: Bearer ...) - для серверних інтеграцій. - Токени користувачів - персональні токени доступу (Laravel Sanctum) чи токени OAuth 2.0.
- Cookie-сесія - для власного SPA на тому самому домені (Sanctum SPA-режим), з CSRF-захистом.
- JWT - самодостатній підписаний токен; сервер перевіряє підпис без запиту до бази, але відкликати такий токен до закінчення терміну складніше.
Авторизація - на кожному ендпоінті, на сервері:
public function update(UpdateOrderRequest $request, Order $order)
{
$this->authorize('update', $order); // чи це замовлення цього користувача
// ...
}
Найпоширеніша вразливість API (OWASP API Top 10, BOLA) - є автентифікація, але немає перевірки прав на конкретний об'єкт: GET /orders/43 повертає чуже замовлення, бо код перевірив лише, що користувач залогінений.
Ще правила: токени лише по HTTPS, ніколи в URL (потраплять у логи й історію браузера), з обмеженим терміном дії й мінімально потрібними правами (scopes).
Браузер дотримується same-origin policy: JavaScript зі сторінки https://shop.example не може прочитати відповідь з https://api.other.example. CORS (Cross-Origin Resource Sharing) - механізм, яким сервер дозволяє браузеру послабити це обмеження для певних джерел.
Access-Control-Allow-Origin: https://shop.example
Access-Control-Allow-Methods: GET, POST, PATCH
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Allow-Credentials: true
Для «непростих» запитів (з Authorization, JSON-тілом, методами PUT/DELETE) браузер спершу надсилає preflight - OPTIONS-запит з питанням, чи можна. Access-Control-Max-Age дозволяє кешувати відповідь preflight.
Чому CORS - не захист API:
- Його виконує браузер.
curl, Postman, скрипт на сервері, мобільний застосунок CORS не перевіряють і отримують відповідь незалежно від заголовків. - CORS захищає користувача в браузері від того, щоб чужий сайт від його імені читав дані з вашого API. Але не захищає сам API від прямих запитів.
- Навіть із забороненим CORS простий запит (
POSTз формою) доходить до сервера й виконується - браузер лише не віддає скрипту відповідь. Тому від CSRF рятують токени й cookieSameSite, а не CORS.
Захист API - це автентифікація, авторизація, ліміти запитів, валідація.
Типові помилки налаштування:
Access-Control-Allow-Origin: *разом з credentials - браузер це не дозволяє.- Віддзеркалення будь-якого
Originз запиту у відповідь разом зAllow-Credentials: true- фактично дозволяє будь-якому сайту читати дані користувача.
OAuth 2.0 дозволяє застосунку отримати доступ до даних користувача в іншому сервісі (Google, GitHub) без його пароля: користувач підтверджує доступ на сторінці сервісу, а застосунок отримує токен з обмеженими правами.
Authorization Code flow:
- Застосунок перенаправляє користувача на сервер авторизації з
client_id,redirect_uri,scopeі випадковимstate. - Користувач входить і погоджується надати доступ.
- Сервер повертає користувача на
redirect_uriз одноразовим кодом і тим самимstate. - Застосунок перевіряє
state(захист від CSRF) і обмінює код на токени прямим запитом сервер-сервер. - Отримує
access_token(короткоживучий) і частоrefresh_token.
PKCE (Proof Key for Code Exchange) захищає від перехоплення коду. Перед кроком 1 застосунок генерує випадковий code_verifier і передає його хеш - code_challenge. На кроці 4 надсилає сам code_verifier. Сервер перевіряє, що хеш збігається, - тож перехоплений код без verifier'а марний.
code_verifier = випадковий рядок 43-128 символів
code_challenge = BASE64URL(SHA256(code_verifier)), method = S256
Чому PKCE тепер обов'язковий скрізь: спершу його придумали для мобільних і SPA-застосунків, які не можуть зберігати client_secret. Сучасні рекомендації (OAuth 2.0 Security BCP, OAuth 2.1) вимагають PKCE і для серверних клієнтів.
Чого не використовувати: Implicit flow (токен одразу в URL) і Resource Owner Password flow (застосунок бере пароль користувача) - обидва вважаються застарілими й небезпечними.
У Laravel: вхід через сторонні сервіси - Socialite (він уже передає state і підтримує PKCE), власний OAuth-сервер - Passport.
Прочитати - ще не значить знати
20 питань, по одному на екран, ~20 хв. Після завершення - розбір кожної помилки з посиланням на питання.