Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

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, часто разом із токенами.

Обов'язкове в будь-якому варіанті: найменші права для кожного сервісу, ротація секретів без простою (два дійсні ключі на час переходу), журнал викликів.

Докладніше в документації: RFC 8705: OAuth 2.0 з 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).

Докладніше в документації: HTTP-автентифікація

Браузер дотримується 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 рятують токени й cookie SameSite, а не CORS.

Захист API - це автентифікація, авторизація, ліміти запитів, валідація.

Типові помилки налаштування:

  • Access-Control-Allow-Origin: * разом з credentials - браузер це не дозволяє.
  • Віддзеркалення будь-якого Origin з запиту у відповідь разом з Allow-Credentials: true - фактично дозволяє будь-якому сайту читати дані користувача.

Докладніше в документації: CORS

OAuth 2.0 дозволяє застосунку отримати доступ до даних користувача в іншому сервісі (Google, GitHub) без його пароля: користувач підтверджує доступ на сторінці сервісу, а застосунок отримує токен з обмеженими правами.

Authorization Code flow:

  1. Застосунок перенаправляє користувача на сервер авторизації з client_id, redirect_uri, scope і випадковим state.
  2. Користувач входить і погоджується надати доступ.
  3. Сервер повертає користувача на redirect_uri з одноразовим кодом і тим самим state.
  4. Застосунок перевіряє state (захист від CSRF) і обмінює код на токени прямим запитом сервер-сервер.
  5. Отримує 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.

Докладніше в документації: PKCE

Прочитати - ще не значить знати

20 питань, по одному на екран, ~20 хв. Після завершення - розбір кожної помилки з посиланням на питання.