Картування словникового запасу автентифікації: API-ключі, сесії, JWT, OAuth2, OIDC, SSO
Дізнайтеся, як API-ключі, сесії, JWT, OAuth2, OpenID Connect та SSO взаємодіють між собою, класифікуючи кожен з них за одним запитанням: хто викликає, або що він може робити.
Розробник додає опцію „Увійти через Google“ до проекту, бачить повернений токен доступу та вважає завдання виконаним. Тоді колега ставить просте запитання: як додаток насправді знає, що це за користувач? Часто чесною відповіддю є те, що він цього не знає, адже була реалізована делегована автентифікація, а не входження. Терміни на кшталт JWT, сесія, OAuth2, OIDC та SSO зазвичай пояснюються окремо, і саме тому вони зливаються між собою. У цій статті кожен з них розміщений на окремій схемі, щоб ви могли зрозуміти, яку проблему насправді вирішує певний елемент вашої технологічної структури, та обрати правильний інструмент для наступного завдання.
Два запитання за кожним механізмом автентифікації
Майже все в цій галузі відповідає на одне з двох запитань.
- Аутентифікація запитує хто ви? Це процес перевірки ідентичності – чи належить вона особі, сервісу на сервері чи пристрою.
- Авторизація запитує що ви маєте право робити? Вона відбувається після підтвердження ідентичності та визначає, які ресурси та дії є доступними.
HTTP вже кодує це розділення у своїх статусних кодах. Відповідь 401 Unauthorized означає, що звернулийся не довів свою ідентичність (попри оманливу назву, йдеться про аутентифікацію). Відповідь 403 Forbidden означає, що сервер точно знає, хто звертається, але все одно відхиляє запит. Якщо ваш API повертає 403 через відсутність токена або 401 через користувача без ролі, клієнти не можуть визначити, чи потрібно запитувати про входження, чи показувати повідомлення „доступ заборонений“.
Фізична аналогія допомагає. Біля входу до офісу охоронець перевіряє вашу карточку співробітника — це автентифікація. Опинившись всередині, ваш пропуск дозволяє відкрити певні двері, а не інші — це авторизація. Один крок встановлює ідентичність, інший надає дозволи, і система може пройти перший крок, але зазнати невдачі при другому. Щоб детальніше дізнатися, де саме має знаходитися кожна перевірка у коді додатку, перегляньте автентифікація проти авторизації та їхнє розташування у коді.
Пам’ятайте про ці два запитання протягом усього ознайомлення. Кожен з наведених нижче механізмів стає зрозумілішим, як тільки ви дізнаєтесь, на яке з запитань він відповідає.
Ключі API: ідентифікація клієнта, а не особи
Клієнтські ключі API є найпростішою формою автентифікації. Провайдер видає вам унікальний рядок, який ви надсилаєте з кожним запитом, а сервер порівнює його з ключами, що зберігаються у нього. Багато API приймають його як облікові дані у стандартному заголовку:
Authorization: Bearer your-api-key-here
Інші провайдери використовують власний заголовок, наприклад X-API-Key; ідея залишається такою ж. Ключ не описує жодної особи – це непрозоре значення, тому сервер мусить його знайти, щоб дізнатися, який обліковий запис здійснює запит. У ньому немає вбудованого терміну дії, якщо ви його не встановите, і той, хто має витеклий ключ, має такі самі права доступу, як і його законний власник. Тому обов’язком користувача є здійснення ротації ключів, визначення їхнього діапазону та безпечне зберігання.
HTTP Basic authentication, яка надсилає ім’я користувача та пароль у форматі Base64 у заголовку, також існує. Base64 — це формат кодування, а не шифрування, тому Basic authentication є безпечною лише через TLS, і її використання мало доцільне поза дуже простими внутрішніми інструментами. API-ключі є більш поширеним та простим у використанні рішенням; вони ідеально підходять, коли ви контролюєте обидві сторони з’єднання або коли сервіс стягує плату та обмежує кількість запитів за рахунком.
Типові ситуації, де використовуються API-ключі:
- API моделей ШІ
- Платіжні шлюзи
- API з метеоданими та іншими даними
- Запити між власними сервісами
Зверніть увагу, чого немає в цьому списку: кінцевих користувачів, які входять у ваш додаток. API-ключ ідентифікує програму чи рахунок, що здійснює запит, а не людину, яка працює за екраном.
Сеанси: стан сервера, який зберігається у куку
Задовго до того, як JWT стали популярними, сеанси були стандартним способом підтримки авторизованого доступу користувачів, і вони залишаються ефективним варіантом для додатків, які обробляються на сервері.
Процес є простим: користувач надсилає свої облікові дані, сервер перевіряє їх та створює запис сеансу в певному сховищі (пам’яті, Redis чи базі даних), а у відповіді встановлюється куки, яка містить лише ідентифікатор сеансу. При кожному наступному запиті браузер надсилає цю куки назад, і сервер знаходить відповідний запис, щоб отримати інформацію про сеанс та приєднаного до нього користувача.
Така архітектура є становою, але саме це є її силою. Сервер зберігає всю інформацію, тому виходження користувача з системи чи скасування компрометованого сеансу полягає лише у видаленні відповідного запису. Сама куки не містить жодної конфіденційної інформації, окрім випадкового ідентифікатора.
Витрати виникають під час горизонтального масштабування. Якщо кілька серверів обробляють трафік, усі вони повинні мати доступ до одного й того ж сховища сеансів; інакше користувач, увійшовши на одному сервері, буде виглядати анонімним на іншому. У практиці цю проблему добре вирішує спільний екземпляр Redis, але це ще один компонент, який потрібно розгорнути, контролювати та налаштувати правильно, а неправильно налаштоване сховище сеансів — це саме та проблема, яка може виникнути у найгірший момент під час випуску продукту.
Сеанси залишаються чудовим варіантом для:
- Традиційних веб-додатків, які генеруються на сервері
- Панелей керування адміністраторами
- Додатків, де миттєве скасування доступу важливіше, ніж відсутність стану
JWT: самодостатній, підписаний та читабельний
JSON Web Token — це підписаний JSON-об’єкт, який містить таку інформацію, як ID користувача, його ролі та час закінчення дії. Він складається з трьох сегментів, закодованих у форматі base64url та з’єднаних крапками:
Header.Payload.Signature
У заголовку вказується алгоритм підписування, у тілі знаходяться заперечення, а підпис дозволяє серверу підтвердити, що жодна з цих частин не була змінена без використання ключа підписування. Оскільки заперечення передаються всередині токена, сервер може автентифікувати запит, перевіривши підпис та прочитавши тіло, без необхідності звертатися до бази даних. Саме ця властивість робить JWT підходящими для безстандартних та розподілених систем.
Підписано — не означає секретне
Найпоширеніше непорозуміння полягає у тому, що JWT приховує свій вміст. Це не так. Стандартний JWT підписується, а не шифрується, і будь-хто, хто його має, може розшифрувати його вміст за допомогою одного лише запиту base64. Ніколи не включайте паролі, особисті дані, які ви не хотіли б показувати користувачеві, чи внутрішні секрети до полів JWT, припускаючи, що вони залишаться приватними. Коли трапляється інцидент, пов’язаний з JWT, саме це хибне припущення часто є його причиною. (За специфікацією JWE існують шифровані токени, але це окремий формат, і вони рідко є тим, що підразуміється під „JWT“ у навчальних матеріалах.)
Токени доступу та оновлення
Оскільки JWT залишається дійсним до закінчення терміну дії, зазвичай використовується два токени:
- Токен доступу: короткочасний, зазвичай від 15 хвилин до години, і надсилається з кожним запитом до API.
Зберігайте токен оновлення у куку HttpOnly, а не у localStorage, де будь-який вставлений скрипт міг би його прочитати. Короткий термін дії токена доступу обмежує наслідки його витоку, тоді як саме у токені оновлення може розміщуватися логіка скасування.
JWT ефективно підходять для API, мобільних клієнтів, односторінкових додатків та розподілених сервісів. Вони не зробили сеанси застарілими; вони обмінюють простоту скасування на відсутність стану, і правильний вибір залежить від вашої архітектури. У статті „Сеанси та JWT як моделі автентифікації в Node.js“ детально розглядається цей компроміс.
OAuth 2.0: делегований доступ, а не вхід
OAuth 2.0 — це концепція, яку найчастіше поміщають не на своє місце. Це рамка авторизації, а не протокол аутентифікації. Її завдання — дозволити одному додатку отримувати доступ до ресурсів, які зберігаються іншою службою від імені користувача, без необхідності того, щоб користувач передавав свій пароль.
У звичайному процесі авторизації за допомогою коду ваш додаток перенаправляє користувача на екран згоди постачальника, де ставиться запитання на кшталт "Чи дозволити цьому додатку переглядати ваші файли у Google Drive?". Якщо користувач погоджується, постачальник перенаправляє його назад із тимчасовим кодом авторизації. Ваш бекенд обмінює цим кодом на токен доступу, а потім використовує його для звернення до API постачальника.
Цей токен доступу є дозволом на отримання доступу до певних ресурсів. Він не є інформацією про те, хто є користувачем, а специфікація OAuth2 не визначає його формату та не вимагає, щоб ваше додаток міг його читати. Вважати наявність токена доступу доказом того, що користувач увійшов у систему, — це класична помилка з початкового сценарію, яка призводила до реальних вразливостей, наприклад коли токен, виданий одному додатку, приймається іншим як доказ особи.
OAuth2 розроблений для відповідей на такі запитання:
- Чи може цей додаток читати файли користувача у Drive?
- Чи може цей додаток отримувати доступ до репозиторіїв користувача?
Він не призначений для відповідей на запитання: хто є цим користувачем?
OpenID Connect: шар ідентифікації поверх OAuth2
Якщо OAuth2 визначає, до чого може отримати доступ додаток, то OpenID Connect додає відсутню інформацію про те, хто є ця особа. OIDC — це простий шар ідентифікації, створений на основі OAuth2 з повторним використанням його механізмів перенаправлення та обміну токенами. Коли ви натискаєте «Увійти через Google», процес, який відбувається за кулисами, ґрунтується на OIDC, а не просто на OAuth2.
Після автентифікації користувача постачальник OIDC повертає два токени з різною функцією:
- Токен ID — це JWT, який описує користувача: стабільний унікальний ідентифікатор, а також зазвичай містить дані, такі як ім’я та електронна пошта.
- Токен доступу — дозволяє вашому додатку викликати API від імені користувача.
Токен ID використовується для автентифікації користувача у вашому додатку. Токен доступу надає право на виконання подальших дій вашим додатком. Ваш бекенд має перевіряти підпис, емітента, цільову аудиторію та термін дії токена ID, перш ніж довіряти його даним, а також прив’язувати облікові записи користувачів до стабільного ідентифікатора, а не до електронної адреси, яка може змінитися. Якщо дивитися з цієї точки зору, OAuth2 сам по собі не може забезпечити входження; OIDC доповнює його.
SSO: користувацький досвід, забезпечений протоколами
Механізм єдиного входження часто плутають із протоколами, які його реалізують. SSO — це шаблон користувацького досвіду: автентифікуватися один раз, а потім переходити між кількома системами без повторного входження. Відомим прикладом є Google: увійшовши один раз, ви можете користуватися Gmail, Drive, Calendar та YouTube без додаткової автентифікації.
Два протоколи виконують основну роботу, яка лежить в основі SSO:
- SAML: базується на XML та є зрілою технологією, яка домінує в корпоративних середовищах, таких як корпоративні портали, панелі керування CRM та внутрішні інструменти.
- OpenID Connect: базується на JSON та JWT, є новішою технологією та зазвичай використовується у веб- та мобільних додатках.
SAML є надзвичайно складною для реалізації та виправлення помилок, тому для нової системи OIDC зазвичай є простішим варіантом. SAML все ще залишається актуальною, коли необхідно інтегруватися з корпоративними постачальниками ідентифікації, які не підтримують OIDC, а таких постачальників досі багато.
Коли хтось просить вас «додати SSO», перше, що потрібно з’ясувати, — це який протокол підтримує їхній постачальник ідентифікації. Саме ця відповідь визначає бібліотеки, налаштування та процес тестування, які будуть проводитися далі.
Вибір механізму
Якщо узагальнити всі фактори, приблизний посібник з вибору виглядає так:
- Ваш сервіс викликається іншими програмами, а не людьми: ключі API з обмеженим діапазоном та можливістю оновлення, або облікові дані клієнта OAuth2 у разі потреби в токенах з терміном дії.
- Додаток, який обробляється на сервері, або панель керування з власним процесом входу: сеанси з безпечним куки типу
HttpOnly. - Безстанові API, мобільні клієнти чи кілька сервісів, які перевіряють одного користувача: токени доступу JWT із токенами оновлення.
- Ваш додаток потребує роботи з даними користувача в іншому сервісі: OAuth 2.0.
- Ви хочете, щоб користувачі входили за допомогою існуючого облікового запису, наприклад Google: OpenID Connect.
- Співробітникам потрібен один процес входу для багатьох внутрішніх чи SaaS-інструментів: єдиний вхід через OIDC або SAML, якщо це вимагає постачальник ідентичності.
Ці опції поєднуються. Типовий продукт може використовувати OIDC для входу, потім створювати власну сесію або JWT, водночас зберігаючи токени доступу OAuth2 для інтеграції з третіми сторонами.
Поширені запитання
У чому різниця між автентифікацією та авторизацією на практиці? Автентифікація перевіряє ідентичність та призводить до помилки HTTP 401; авторизація перевіряє права та призводить до помилки 403. Це окремі кроки з різними кодами помилок, і їх поєднання створює справжні прогалини в безпеці.
Чи варто використовувати JWT чи сесії? Сесії ідеально підходять для додатків, які обробляються на сервері та можуть ділитися сховищем сесій. JWT мають більше сенсу для безстанових API, мобільних клієнтів та розподілених систем. Вирішуйте, виходячи з вашої архітектури та потреб у скасуванні, а не з того, який варіант здається сучаснішим.
Чи призначений OAuth2 для автентифікації чи авторизації? Авторизація. Він дозволяє додаткам отримувати доступ до ресурсів від імені користувача без підтвердження його особи. Для ідентифікації використовуйте OpenID Connect, який розширює OAuth2 саме з цією метою.
Основні висновки
- Класифікуйте кожен механізм за одним запитанням: хто викликає? чи що вони можуть робити?
- Ключі API ідентифікують клієнтські додатки; вони не містять інформації про особу користувача та потребують власних правил закінчення терміну дії та їх оновлення.
- Сеанси зберігають стан на сервері, що полегшує скасування доступу, але вимагає спільного сховища у масштабних системах.
- JWT підписуються, а не шифруються; тримайте секрети поза вмістом повідомлення та токени оновлення поза
localStorage. - OAuth2 надає делегований доступ; OIDC додає токен ID, який фактично автентифікує користувача.
Більшість плутанини в цій галузі, включаючи непорозуміння щодо опції „Увійти через Google“, виникає через змішування цих двох питань. Треба розділяти питання ідентичності та дозволів; тоді вибір між цими інструментами стає справою підбору кожного з них під конкретне завдання, на яке він призначений.