Ця стаття присвячена моделі найкращих практик, заснованій на архітектурі з використанням JWT (JSON Web Token) — поширеному підході до аутентифікації користувачів у сучасних системах.

Що таке аутентифікація?

Аутентифікація у взаємодії між клієнтом і системою — це процес, що дозволяє системі перевірити ідентичність клієнта, де клієнтом може бути машина, людина або інтерфейс машини, що керується людиною, а система, як правило, є віддаленим додатком. Ідентичність клієнта зазвичай відповідає конкретному користувачеві, який уже зареєстрований у системі. Отже, аутентифікація, по суті, полягає у підтвердженні того, чи є запитувач дійсно власником облікового запису з попередньо визначеного списку користувачів. Цей процес реєстрації часто вимагає створення пароля або ключа API, який виступає спільним секретом між користувачем (будь то машина чи людина) та системою. Таким чином, автентифікація в основному слугує для встановлення особи, що відрізняється від присвоєння атрибутів, ролей або дозволів у системі.

Що таке авторизація?

Після встановлення ідентичності користувача в системі стає необхідним перевіряти його запити на відповідність наданим йому правам. Ці права можуть бути організовані у різних формах, таких як ролі, дозволи або конкретні обмеження щодо доступу до певних ресурсів у додатку. Часто авторизація також передбачає обмеження користувачів їхньою власною «пісочницею» — поширеною практикою в багатьох SaaS-додатках, де користувачі можуть взаємодіяти лише зі своїми ресурсами. Зазвичай додаток використовує записи бази даних, щоб пов’язати користувачів із заздалегідь визначеними ролями або правами доступу до певних ресурсів. У середовищі мікросервісів управління цими дозволами часто здійснює спеціальна служба авторизації, особливо коли бізнес-логіка вимагає детального контролю доступу.

Основи логіки аутентифікації

Аутентифікація користувача в першу чергу ґрунтується на секреті, відомому як користувачеві, так і системі. Зазвичай це передбачає введення користувачем унікального імені користувача (часто — адреси електронної пошти) разом із паролем. Потім система перевіряє, чи існує це ім’я користувача в її базі даних і чи збігається вказаний пароль із тим, що зберігається в системі. У деяких випадках замість пароля використовується ключ API. Цей ключ зазвичай генерується в результаті безпечного процесу автентифікації і слугує подвійним підтвердженням ідентичності користувача та спільного секрету між користувачем і системою. Ключі API особливо поширені в сценаріях, що забезпечують доступ між машинами, на відміну від запитів, ініційованих людьми.

По суті, логіка автентифікації зосереджена на підтвердженні особи користувача для кожного запиту на доступ до системи. Це означає, що клієнт повинен надавати підтвердження своєї особи, наприклад пароль або ключ API, з кожним запитом. Відповідно, система за своєю суттю не може пов’язати наступні запити від клієнта з попередніми як автентифіковані, навіть якщо ці запити надходять з дуже короткими інтервалами. Тому немає гарантії, що запити, які надходять після початкового автентифікованого запиту, надходять від того самого перевіреного користувача.

Навіщо потрібні токени?

Концепція токенів виступає як важливе рішення в процесі автентифікації користувачів. Перевірка особистісних даних користувача під час кожного запиту — звернення до бази даних, зіставлення хеш-значень паролів — вимагає значних системних ресурсів. Крім того, багаторазове введення імені користувача та пароля для кожного запиту може бути обтяжливим для користувачів. Щоб вирішити ці проблеми, більшість систем використовують процес, за якого ідентичність користувача перевіряється лише один раз під час реєстрації або входу в систему. Після цього система генерує токен як більш ефективний та зручний для користувача додатковий доказ ідентичності. Цей токен, відомий як токен доступу, видається користувачеві під час його першого входу в систему і повинен надаватися при подальших запитах.

Токени доступу є безпечним методом зберігання даних користувача, що дозволяє безпечно зберігати їх на локальному пристрої користувача. Це полегшує автоматичну передачу токена з кожним запитом, що значно покращує користувацький досвід. Такий підхід часто зустрічається в сучасних веб-додатках, де користувачі входять у систему один раз, а потім безперешкодно продовжують взаємодіяти з додатком, при цьому токен доступу керує їхньою автентифікацією у фоновому режимі.

Чим токени є безпечними?

Токени стали безпечним методом перевірки ідентичності в галузі розробки програмного забезпечення. Спочатку токени були зашифрованими пакетами даних, що містили інформацію про користувача, і для клієнта виглядали як беззмістовний набір символів. Це шифрування гарантувало, що дані токена залишалися захищеними від маніпуляцій. Зашифровані токени інкапсулюють ідентичність клієнта і видаються лише після того, як клієнт надає первинне підтвердження своєї ідентичності (наприклад, пароль або ключ API). Оскільки ці токени містять дані про особу користувача, будь-який наступний запит, що супроводжується токеном, фактично підтверджує первинну особу користувача. Основою цієї системи є шифрування, яке виступає захистом від маніпуляцій з даними, тим самим забезпечуючи безпеку та цілісність процесу автентифікації.

Шифрування токена в процесах автентифікації насамперед спрямовано не на приховування конфіденційної інформації користувача, а на забезпечення незмінності токена. Цей захід безпеки запобігає створенню підроблених токенів сторонніми особами. Завдяки шифруванню токена система ефективно захищає від можливості імітації поведінки токена зовнішніми суб’єктами, тим самим зберігаючи цілісність і надійність процесу автентифікації. Такий підхід гарантує, що визнаються та вважаються надійними лише токени, видані системою автентифікації, що захищає від шахрайських спроб отримати доступ до системи.

schema1

Рисунок 1: Діаграма послідовності сценарію, в якому створення та перевірка токена здійснюються в службі автентифікації. Для кожного запиту мікросервіси повинні звертатися до служби автентифікації з запитом на перевірку токена.

Кращий спосіб захисту токена: цифровий підпис

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

І шифрування, і підписання вимагають секретного ключа, відомого лише системі, що гарантує неможливість фальсифікації даних. Однак така залежність від єдиного секретного ключа створює потенційні вразливості в розподіленій системі, де ключ має бути спільним для різних компонентів системи, які можуть бути географічно віддаленими один від одного. У разі шифрування для розшифрування токена потрібен секретний ключ. Одним із рішень є постійне розшифрування токена через службу користувача, яка володіє ключем, що, однак, може призвести до проблем із продуктивністю та збільшення затримок. Як альтернатива, ключ можна розподілити між іншими службами, що дозволить їм самостійно виконувати розшифрування або перевірку. Цей сценарій підкреслює компроміси між безпекою та практичністю в системах автентифікації на основі токенів.

Ця дилема стає очевидною при використанні цифрових підписів з одним захищеним ключем як для підписання, так і для перевірки токенів. У архітектурах мікросервісів найбезпечнішим методом управління токенами доступу з цифровими підписами є використання пари ключів, які в криптографії зазвичай називають приватним і відкритим ключами. Токен підписується закритим ключем, який зберігається в таємниці й відомий лише службі-користувачу (емітенту токена). Цей підписаний токен може бути перевірений будь-ким, хто володіє відповідним відкритим ключем, який є загальнодоступним. Важливо, що завдяки математичним та логічним принципам, на яких це ґрунтується, відкритий ключ може перевіряти підпис, зроблений закритим ключем, але не може використовуватися для генерації самого цифрового підпису. Такий підхід ефективно поєднує безпеку та практичність у розподілених системах.

schema2

Рисунок 2: Діаграма послідовності сценарію, в якому перевірка токена здійснюється в мікросервісах за допомогою відкритого ключа. Кожен запит може оброблятися в мікросервісах без звернення до сервісу користувача.

Сесії користувачів

Сесії користувачів — це модель, що охоплює всі дії користувача, які відбуваються між операціями входу в систему. Кожен вхід ініціює нову сесію, яку представляє відповідний токен. У певних сценаріях токени можуть містити додаткову інформацію про користувача, таку як атрибути та права. Однак поширеною практикою є збереження токенів якомога лаконічнішими, поєднуючи їх із більш детальним серверним представленням сесії. Звідси випливає поняття «сеансів користувачів» — детальних даних про користувача, що увійшов у систему, які зберігаються в серверному сховищі сеансів, такому як база даних або Redis. У архітектурі мікросервісів централізоване розміщення сховища сеансів, доступного для всіх сервісів, дозволяє цим сервісам отримувати доступ до детальних даних користувача без необхідності багаторазового звернення до сервісу користувача.

Ще одна важлива роль сеансів користувачів полягає у забезпеченні безперервної дійсності токенів автентифікації. Вони виступають як додатковий механізм перевірки, що підтверджує: токен не втратив чинності внаслідок таких дій, як вихід користувача з системи або блокування облікового запису. Це схоже на вилучення ключів у особи, яка виявилася ненадійною. У середовищі мікросервісів сервіс користувача припиняє сесію в сховищі після виходу користувача з системи або його блокування. Це повідомляє інші сервіси про втрату чинності, запобігаючи обробці ними запитів із використанням застарілого токена. Отже, сесії користувачів слугують важливим рівнем безпеки та управління даними в розподілених системах.

Використання атрибутів, пов’язаних з користувачем, у середовищі мікросервісів

Після проходження автентифікації кожен мікросервіс у рамках додатка має безпечний доступ до інформації про особу користувача, яка зазвичай представлена цілим числом або UUID і часто називається UserId. Цей UserId дозволяє мікросервісам пов’язувати дії користувача (або зміни стану) з автентифікованим користувачем. Наприклад, щойно створене замовлення можна позначити UserId, щоб вказати його власника. Він також дозволяє здійснювати контроль доступу з урахуванням конкретного користувача, наприклад, відображати в історії замовлень лише ті замовлення, що пов’язані з цим UserId. Крім того, система може використовувати UserId для перевірки прав користувача перед тим, як дозволити певні дії, такі як створення товару, виходячи з ролі, призначеної користувачеві.

У монолітному додатку отримання атрибутів прав користувача, таких як ролі чи дозволи, зазвичай є простим процесом, що часто передбачає виконання простого запиту до бази даних. Однак у середовищі мікросервісів доступ до цієї інформації стає складнішим. Дані про доступ користувачів зазвичай управляються спеціалізованими сервісами, такими як сервіс дозволів або авторизації, а іноді — самим сервісом користувача. Отже, коли клієнт надсилає запит на виконання дії, самої аутентифікації недостатньо для авторизації цієї дії. Потрібен додатковий процес авторизації. Цей процес, що має вирішальне значення для прийняття рішень щодо дій користувачів, створює унікальні виклики в архітектурі на основі мікросервісів, оскільки передбачає координацію та взаємодію між різними сервісами.

schema3

Рисунок 3: Діаграма послідовності для сценарію, в якому перевірка токена здійснюється в мікросервісах із додатковою перевіркою сховища сеансів. Об’єкт сеансу в цьому випадку містить детальні атрибути користувача та інформацію про поточну дійсність токена.

Авторизація: детальний огляд

У середовищі мікросервісів авторизація користувачів для виконання конкретних дій може відбуватися за одним із двох логічних алгоритмів. Перший передбачає безпосередній запит до служби авторизації. Це необхідно, коли права доступу базуються на складній, детально налагодженій логіці, наприклад, при успадкуванні прав доступу від батьківських каталогів до файлів. У таких випадках спеціалізований мікросервіс обробляє всю логіку авторизації. Кожна служба повинна звертатися до цієї служби авторизації перед виконанням дії для користувача. Такі служби авторизації повинні мати високу продуктивність та використовувати API gRPC для забезпечення ефективності. Для складних сценаріїв контролю доступу також доступні сторонні інструменти.

Другий логічний алгоритм застосовується, коли авторизація користувача ґрунтується на заздалегідь визначених ролях або конкретних дозволах, пов’язаних із певними діями. Якщо обсяг даних про ролі та дозволи користувача є керованим, оптимальною стратегією є зберігання цієї інформації в даних сеансу у сховищі сеансів. Таким чином, коли мікросервісу потрібні ролі або дозволи користувача, він може легко отримати їх зі сховища сеансів, оминаючи необхідність використання зовнішньої служби авторизації. Цей метод підходить для складних веб-додатків, таких як інтернет-магазин Amazon або Medium, де авторизація здебільшого стосується даних, що належать користувачеві, а не детальних ролей чи дозволів. У цьому випадку сховище сеансів слугує ефективним центром авторизації для ролей і дозволів, якщо тільки не потрібна більш складна бізнес-логіка авторизації або великі набори даних про дозволи.

Навіть у найскладніших системах авторизація користувачів часто зводиться до поняття власності на ресурси. Для таких платформ, як Amazon чи Medium, головним питанням зазвичай є те, чи має користувач законне право власності або доступ до відповідних ресурсів — наприклад, до замовлень на Amazon або статей на Medium. У таких сценаріях авторизація користувачів пов’язана не стільки зі складністю ролей чи дозволів, скільки з перевіркою зв’язку між користувачем і ресурсом. Це означає, що в багатьох випадках для авторизації дій щодо цих ресурсів достатньо простої аутентифікації особи користувача — підтвердження того, що він є тим, за кого себе видає. Ідентифікатор аутентифікованого користувача стає ключовим фактором у визначенні того, чи слід надати користувачеві доступ до певного ресурсу або можливість його модифікації, що значно спрощує процес авторизації в системах, де право власності на ресурси є основним критерієм контролю доступу.

Найкраща практика аутентифікації та авторизації користувачів у мікросервісній архітектурі

  1. Користувач ініціює запит на вхід, вказавши свою електронну адресу та пароль, який надсилається до сервісу користувачів.

  2. Сервіс користувачів перевіряє електронну адресу користувача за базою даних, а також порівнює хеш пароля з наданим хешем пароля.

  3. Після успішної перевірки сервіс користувачів генерує токен JWT, підписуючи його приватним ключем, який надійно зберігається на сервері. Цей токен містить userId та випадково згенерований sessionId.

  4. Потім сервіс користувачів створює новий об’єкт сесії у форматі JSON у сховищі сесій Redis, що містить усі відповідні дані користувача разом із userId та sessionId.

  5. Сервіс користувачів публікує подію «user-session-created» у брокері повідомлень Kafka.

  6. Сервіс користувачів повертає підписаний токен JWT клієнту — у файлі cookie або в тілі відповіді.

  7. Служба авторизації очікує на подію «user-session-created» і додає до сеансу користувача додаткову інформацію, таку як ролі, придбані розширення або конкретні дозволи, якщо вони визначені.

  8. Користувач надсилає наступний запит на сервер ресурсів (наприклад, сервер статей, щоб створити нову статтю), включаючи підписаний токен JWT.

  9. Служба ресурсів перевіряє підпис токена JWT за допомогою відкритого ключа служби користувачів, який доступний для всіх служб.

  10. Після аутентифікації служба підтверджує, що userId відповідає даному користувачеві. Використовуючи sessionId з токена, вона отримує з сховища сеансів об’єкт сеансу, який містить детальну інформацію про користувача. Якщо відповідного об’єкта сеансу не існує, запит відхиляється.

  11. Отримавши доступ до даних сесії, сервер ресурсів перевіряє авторизацію користувача на виконання конкретної дії (наприклад, перевірка статусу преміум-користувача для створення статті) та виконує необхідні дії над ресурсами, якими він керує.

  12. Потім сервіс ресурсів повідомляє користувачеві про результат дії.

=== img ===

Рисунок 4: Детальна діаграма послідовності, що ілюструє найкращі практики аутентифікації та авторизації. Для аутентифікації використовуються JWT-токен, підписані цифровим підписом із застосуванням парних ключів, а об’єкт сеансу забезпечує безперервну дійсність токена, містячи детальні атрибути користувача та його права, що дозволяють надавати користувачеві дозвіл на виконання конкретних дій.