JWT срещу сесия: кой механизъм за удостоверяване да изберете?

Да удостоверите потребител означава при всяка заявка да знаете кой е той. Сблъскват се две основни семейства: сървърните сесии, при които сървърът пази следа за свързания потребител, и JSON Web Tokens (JWT), при които самоличността пътува в подписан токен, носен от клиента. Изборът влияе върху сигурността, способността за мащабиране и лекотата при изключване на потребител. Ето как да решите според вашия контекст.

Сървърните сесии (със състояние)

При сесия сървърът създава идентификатор на сесия при свързване, съхранява свързаните данни (самоличност, роли, кошница) от страната на сървъра (памет, Redis, база данни) и връща на браузъра бисквитка, съдържаща само този идентификатор. При всяка заявка сървърът прочита бисквитката, намира сесията и знае кой говори.

  • Състояние от страна на сървъра: източникът на истина остава на сървъра, клиентът носи само непрозрачна препратка.
  • Незабавна отмяна: изтриването на сесията от страна на сървъра изключва потребителя мигновено.
  • Бисквитка: предавана автоматично от браузъра, в идеалния случай с HttpOnly, Secure и SameSite.

JSON Web Tokens (без състояние)

JWT е самоносещ токен, съставен от три части, кодирани в base64url и разделени с точки: header, payload (claims, например идентификатор на потребителя и изтичане) и signature. Сървърът подписва токена при свързване; след това му е достатъчно да провери подписа, за да се довери на съдържанието, без да съхранява нищо.

  • Без състояние: цялата необходима информация е в токена, сървърът няма нужда от споделена памет.
  • Проверим навсякъде: всяка услуга, която знае ключа, може да валидира токена, удобно за разпределени архитектури и SSO.
  • Инструменти: можете да инспектирате токен с нашия JWT декодер, да проверите подписа му с JWT верификатора или да създадете такъв с JWT генератора.

Сравнителна таблица

Критерий Сървърна сесия JWT
СъстояниеСъс състояние (съхранявано на сървъра)Без състояние (носено от клиента)
Сървърно съхранениеНеобходимо (Redis, база)Никакво
ОтмянаНезабавнаТрудна преди изтичане
Хоризонтална мащабируемостНужно е споделено хранилищеЕстествена
Предаван размерМалък (един идентификатор)По-голям (подписани claims)
Между домейни / SSOОграничаващоПодходящо
Повърхност за XSSНиска при HttpOnly бисквиткаВисока при съхранение в localStorage

Сигурност: XSS, CSRF и отмяна

Двата подхода са сигурни, ако са правилно реализирани, но рисковете им се различават.

  • XSS: бисквитка за сесия с HttpOnly е недостъпна за JavaScript, тоест защитена от кражба чрез инжекция. JWT, съхранен в localStorage, обаче е четим от всеки скрипт, което го прави първокласна цел. Съхраняването на JWT в HttpOnly бисквитка анулира това предимство на JWT, но връща риска от CSRF.
  • CSRF: бисквитките се изпращат автоматично, тоест са уязвими на CSRF без защита (атрибут SameSite, анти-CSRF токен). JWT, изпратен ръчно в заглавката Authorization, не е засегнат.
  • Отмяна: това е слабото място на JWT. Тъй като е самоносещ, не може да бъде анулиран преди изтичането си без връщане на сървърно състояние (списък за отмяна, черен списък). Сесията се изтрива мигновено.

Мащабируемост и архитектура

На един сървър сесиите са тривиални. Щом разпределите натоварването върху няколко инстанции, всяка инстанция трябва да има достъп до сесиите: нужно е споделено хранилище (Redis) или sticky сесии. Тук JWT блести, защото всяка инстанция валидира токена без мрежово обаждане или общо съхранение.

  • Микросервиси: JWT предава самоличността от една услуга на друга без централна база.
  • Публични и мобилни API: JWT избягва управлението на бисквитки от страна на нативния клиент.
  • Класически монолит: сесията остава по-проста и по-сигурна по подразбиране.

Кога да изберете едното или другото

Изберете сесии, когато

  • Разработвате класическо уеб приложение с рендиране от страна на сървъра
  • Незабавната отмяна е критична (банки, здравеопазване, back-office)
  • Искате най-сигурното решение по подразбиране, с най-малко капани
  • Вашата инфраструктура издържа на споделено хранилище за сесии без болка

Изберете JWT, когато

  • Излагате API, консумиран от SPA, мобилни приложения или трети страни
  • Имате микросервизна архитектура или SSO между домейни
  • Трябва да мащабирате хоризонтално без споделено хранилище
  • Приемате да управлявате кратко изтичане и обновяване на токените

Препоръка

За мнозинството уеб приложения сървърните сесии остават най-сигурният и най-прост избор: незабавна отмяна, HttpOnly бисквитка и нула управление на токени от страна на клиента. Запазете JWT за случаите, в които липсата му на състояние носи истинска стойност: stateless API, мобилни приложения, микросервиси, SSO.

Ако изберете JWT, поддържайте кратък живот (няколко минути) в съчетание с refresh token, съхранен в HttpOnly бисквитка, и предвидете списък за отмяна за чувствителните случаи. Така съчетавате най-доброто от двата свята.

Често задавани въпроси

Шифрован ли е JWT?

Не, по подразбиране JWT е само подписан, не шифрован. Неговият payload е кодиран в base64url и четим от всеки, който го прихване. Никога не поставяйте чувствителни данни в явен вид в JWT. За да шифровате съдържанието, трябва да прибегнете до JWE (JSON Web Encryption).

Къде да съхранявате JWT от страна на клиента?

Най-сигурно е в HttpOnly, Secure и SameSite бисквитка, която защитава от кражба чрез XSS. localStorage е по-просто, но излага токена на всеки злонамерен скрипт. Избягвайте го за токени с високи привилегии.

Как да изключите потребител с JWT?

Тъй като токенът е самоносещ, реалното изключване изисква или да изчакате изтичането му, или да поддържате списък за отмяна от страна на сървъра. Затова се използват кратки срокове на живот и refresh token, който пък може да бъде отменен.

Може ли да се комбинират сесии и JWT?

Да, това е често срещана практика: access токен JWT с кратък живот за API повикванията и refresh token, управляван като сесия (съхраняван и отменяем от страна на сървъра), за да се подновява access токенът.