JWT (JSON Web Token) - токен з трьох частин у Base64URL, розділених крапками: заголовок.дані.підпис.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiIsImV4cCI6MTc2MDAwMDAwMH0.Sfl...
- заголовок - алгоритм підпису (
alg); - дані (claims) -
sub(користувач),exp(термін дії),iss,aud, ролі тощо; - підпис - гарантує, що дані не змінено.
Ключова властивість: сервер може перевірити токен без звернення до бази - достатньо ключа. Звідси популярність у мікросервісах.
Що варто розуміти:
- JWT не шифрований (якщо це не JWE) - будь-хто прочитає дані, декодувавши Base64. Не кладіть туди персональних даних і секретів;
- підпис ≠ секретність: підпис захищає від зміни, а не від читання.
Типові вразливості (RFC 8725 описує найкращі практики):
alg: none- бібліотека, що приймає непідписані токени. Сервер має жорстко задавати дозволені алгоритми, а не брати їх із заголовка токена;- плутанина алгоритмів: сервер очікує RS256 (асиметричний), а зловмисник підписує HS256, використавши публічний ключ як секрет. Захист - той самий: явний список алгоритмів;
- слабкий секрет HS256 - коротку фразу перебирають офлайн;
- відсутня перевірка
exp,aud,iss- токен з іншого сервісу чи прострочений приймається; - неможливість відкликання: stateless-токен діє до
exp, навіть якщо користувач вийшов чи його заблоковано. Ліки - короткий термін (5-15 хвилин) + refresh-токен, або чорний список (jti) - що повертає звернення до сховища; - зберігання в
localStorage- крадіжка через XSS.
Коли JWT не потрібен: для власного застосунку з одним бекендом - звичайна сесія чи непрозорий токен у базі (як Sanctum) простіші: відкликання миттєве, у токені немає даних, бібліотеки не потрібні.
Коли доречний: кілька сервісів перевіряють один токен без спільної бази; OAuth/OpenID Connect (ID-токени - це JWT); короткоживучі підписані посилання.
У Laravel: Sanctum використовує непрозорі токени з хешем у базі; Passport (OAuth2-сервер) видає JWT-токени доступу.