GH GambleHub

JWT: структура та вразливості

1) Що таке JWT і де він використовується

JWT - компактний самодостатній контейнер тверджень (claims) у форматі'Base64Url (header). Base64Url(payload). Base64Url(signature)`.

Використовується для:
  • JWS (підписані токени - справжність/цілісність),
  • JWE (зашифровані токени - конфіденційність),
  • OIDC/OAuth2 як access/ID токени, а також service-to-service аутентифікації.

Плюси: автономність, кешованість, малі накладні витрати. Мінуси: ризик неправильної валідації, складні кейси відгуку.

2) Структура JWT

2. 1 Заголовок (header, JSON)

Мінімально: алгоритм та ідентифікатор ключа.

json
{ "alg": "ES256", "kid": "jwt-2025-10", "typ": "JWT" }

`alg`: алгоритм підпису/шифрування (RS256/ES256/PS256/HS256 тощо).
`kid`: покажчик на ключ (для JWKS-ротації).
Опціональні джерела ключів: «jku», «x5u» (див. уразливості § 6. 3).

2. 2 Корисне навантаження (payload, JSON)

Стандартизовані клейми:
  • `iss` (issuer), `aud` (audience), `sub` (subject)
  • 'exp'( час закінчення),'nbf'( не раніше),'iat'( виданий)
  • «jti» (ідентифікатор токена, придатний для відгуків)
  • domain-клейми: `scope/roles`, `tenant`, `kyc_level` и т. п.

2. 3 Підпис (signature)

JWS = `sign(base64url(header) + "." + base64url(payload), private_key)`

Перевірка: строго відповідним публічним ключем і рівно тим алгоритмом, який сервер очікує.

3) Базові інваріанти перевірки

1. Алгоритм фіксується конфігурацією ресурс-сервера (allow-list), а не довіряється вмісту'header. alg`.
2. Перевіряти'iss'і'aud'на точний збіг,'exp/nbf'- з урахуванням невеликого'clock _ skew'( ± 30-60s).
3. Відмовляти токени без'kid'тільки якщо єдиний ключ і немає ротації; інакше - вимагати «kid».
4. Не довіряти ніяким клеймам без авторизації на об'єктному рівні (BOLA-first).
5. Парсинг - після криптопровірки; базові перевірки розміру до декодування.

4) JWS vs JWE

JWS: підписаний, але читаємо. Не поміщайте в payload PII/секрети.
JWE: шифрує payload; складніше інтеграція, ключова модель критична.
У більшості API досить JWS + заборона на чутливі дані в payload.

5) Життєвий цикл токена

Access: короткий термін (5-30 хвилин).
Refresh: триваліше (7-30 днів), rotate-on-use (одноразовий), зберігати «чорний список»'jti/sid'.
Revocation: списки'jti'з TTL, інтроспекція для opaque-токенів, скорочення'exp'при інцидентах.
Ротація ключів: JWKS з перекриттям (старий + новий), див. статтю «Ротація ключів».

6) Часті вразливості і як їх закривати

6. 1'alg = none '/підміна алгоритму

Суть: сервер довіряє полю'alg'і приймає непідписаний токен.
Захист: жорсткий allow-list алгоритмів на сервері; відхиляти'none'і несподівані значення.

6. 2 RS256→HS256 swap (симетризація)

Суть: зловмисник підміняє'alg'на HS256 і використовує публічний ключ як HMAC-секрет.
Захист: прив'язати ключ до алгоритму конфігурацією; не змішувати симетричні/асиметричні провайдери в одному'kid'.

6. 3 Ін'єкція ключів ('kid/jku/x5u')

Сценарії:
  • «jku» вказує на контрольований зловмисником JWKS (підсуне свій ключ).
  • 'x5u '/' x5c'зловживання зовнішніми сертифікатами.
  • 'kid'з ін'єкцією шляху/SQL ('" ../../privkey. pem"'або'"'OR 1 = 1 -- "').
Захист:
  • Ігнорувати видалені'jku/x5u'або фільтрувати по суворому allow-list доменів.
  • 'kid'використовувати тільки як ключ в локальному каталозі (таблиця/кеш), без файлових шляхів/SQL конкатенацій.
  • JWKS вантажити з довірених URL, короткий TTL, підпис/пінning каналу.

6. 4 Слабкі секрети HS256 (брутфорс)

Суть: HMAC-секрет короткий/витік → підміна підпису.
Захист: використовувати асиметрію (RS/ES/PS) або довжину секрету ≥ 256 біт, секрети - тільки в KMS.

6. 5 Відсутні/невалідні клейми

Ні'aud '/' iss '/' exp'→ токен крос-сервісно придатний або нескінченний.
Занадто довгий'exp'→ ризик компрометації.
Захист: вимагати повний набір клеймів,'exp'короткий,'nbf '/' iat'валідувати з'clock _ skew'.

6. 6 Replay і крадіжка токена

Суть: перехоплення/повтор токена (витік в логах, XSS, MitM без TLS).

Захист:
  • TLS везде, `Secure`+`HttpOnly` cookie, SameSite=Lax/Strict.
  • DPoP/PoP (прив'язка токена до клієнтського ключа) і/або mTLS для партнерів.
  • Короткі'exp', refresh-ротація, device-binding.

6. 7 Витоки через XSS/сховище

Суть: зберігання JWT в'localStorage '/' sessionStorage'→ доступний JS.
Захист: зберігати access-токени в HttpOnly-cookie (якщо можлива cookie-модель) + сувора CSP/Trusted Types.
Для SPA без cookie - ізолювати токен в пам'яті, мінімально жити, захищати від XSS.

6. 8 CSRF

Суть: при cookie-сесіях запити від стороннього сайту.
Захист: SameSite, anti-CSRF токени (double submit),'Origin/Referer'перевірка, Fetch-Metadata фільтри.

6. 9 Oversize/зловживання розміром

Суть: величезні payload/заголовки, DoS на парсинг.
Захист: ліміти розмірів заголовків/тіла, early-reject 431/413, фікс-набір клеймів.

6. 10 Підміна'typ '/' cty '

Суть: плутанина типів ('typ: «JWT»'), вкладені JOSE-об'єкти.
Захист: ігнорувати'typ/cty'для безпеки, покладатися на фіксовані алгоритми і схему клеймів.

7) Зберігання і передача токенів

7. 1 Серверні API (machine-to-machine)

mTLS/HMAC, бажано асиметрія для JWT, канали - через mesh.
Сховище ключів - KMS/HSM, ротація за розкладом, JWKS з перекриттям.

7. 2 Браузерні клієнти

HttpOnly Secure Cookie для access/refresh; короткі TTL; оновлення - через «silent refresh» з'SameSite = None'тільки під HTTPS.
Сувора CSP, Trusted Types, захистити від XSS; для SPA - по можливості не зберігати токен на диску.

8) JWKS, ротація та відгук

JWKS публікується авторизатором; споживачі кешують на 5-15 хвилин.
План ротації: додати новий'kid'→ почати їм підписувати → через N днів видалити старий з JWKS → purge.
Відгук: списки'jti/sid'c TTL; при інциденті тимчасово скоротити'exp'і форс-logout (інвалідувати refresh).

9) Multi-tenant і мінімізація даних

Включати'tenant '/' org'в токен тільки якщо потрібно по архітектурі; інакше підтягувати атрибути з PDP по'sub'.
Ніяких PII; мінімальний набір клеймів → менший ризик витоків і кореляції.

10) Практичний чек-лист валідації (сервер ресурсу)

  • Парсимо тільки після перевірки підпису і базових лімітів розміру.
  • 'alg'з конфігурації; відхиляти несподівані.
  • Перевірити'iss'∧'aud'∧'exp'∧'nbf'∧'iat'( з'clock _ skew').
  • Перевірити'kid'по локальному каталогу/JWKS (короткий TTL).
  • Відфільтрувати/нормалізувати зовнішні покажчики ('jku/x5u'- тільки allow-list).
  • Обмежити довжину/склад клеймів (схема).
  • Застосувати об'єктну авторизацію на ресурс (BOLA-first).
  • Логувати'kid','sub','aud','iss','jti','exp','tenant','trace _ id'( без PII).
  • Метрики помилок підпису, прострочень, аудиту ротації.

11) Приклади безпечних політик (псевдо)

11. 1 Конфігурація очікувань алгоритму

yaml jwt:
expected_issuer: "https://auth. example. com"
expected_audience: ["wallet-service"]
allowed_algs: ["ES256"] # fix the jwks_url: "https ://auth. example. com/.well-known/jwks. json"
jwks_cache_ttl: 600s clock_skew: 60s required_claims: ["iss","aud","sub","exp","iat"]

11. 2 Відмова від віддалених'jku/x5u '

yaml reject_untrusted_key_sources: true allowed_jku_hosts: ["auth. example. com"] # if absolutely necessary

11. 3 Приклад списку відгуків (Redis)

pseudo if redis. exists("revoke:jti:" + jti) then deny()
if now() > exp then deny()

12) Спостережуваність і форензика

Метрики: `jwt_verify_fail_total{reason}`, `jwt_expired_total`, `jwks_refresh_total`, доля `kid`.
Логи (структуровані): 'iss/aud/sub/kid/jti/exp/tenant/trace _ id', причина відмови.
Дашборди: «закінчуються скоро», сплеск помилок валідації, розподіл по регіонах/клієнтам.
Алерти: зростання'verify _ fail'( підпис/алгоритм), помилок JWKS, частки прострочених токенів.

13) Антипатерни

Довіряти'alg'з токена; підтримувати'none'.
Довгоживучі access-токени і відсутність refresh-ротації.
HS256 з коротким секретом/секрет в ENV без KMS.
Приймати'jku/x5u'з будь-якого домену; динамічно завантажувати JWKS без allow-list.
Класти PII/секрети в payload JWS.
Зберігати токени в'localStorage'при наявності XSS-ризиків.
Заглушати помилки валідації (повертати 200 з «soft error»).
Відсутність BOLA-перевірок і покладання тільки на'scope/role'.

14) Специфіка iGaming/фінансів

Клейми: 'kyc _ level','risk _ tier','tenant', строгі'aud'.
Короткі TTL для write-операцій (депозити/висновки), PoP/DPoP або mTLS для критичних маршрутів.
Регуляторний аудит: незмінювані журнали входів/відмов, зберігання логів в межах регіону.
У партнерів/PSP - окремі ключі/' aud', пер-тенант ключі і окремі JWKS.

15) Чек-лист prod-готовності

  • Жорсткий allow-list алгоритмів;'none'заборонений.
  • 'iss/aud/exp/nbf/iat/jti'перевіряються; короткі'exp'.
  • JWKS з перекриттям, короткий TTL кеша; моніторинг часток'kid'.
  • Refresh — rotate-on-use; списки'jti/sid'з TTL; плейбук відгуків.
  • PoP/DPoP або mTLS на критичних маршрутах.
  • HttpOnly-cookies для браузера, CSP/Trusted Types проти XSS; CSRF-захист.
  • Без PII в payload; Мінімальний набір клеймів.
  • Метрики/логи з валідації та відмов; алерти JWKS/verify_fail.
  • Тести негативних сценаріїв: RS→HS swap,'kid'-ін'єкції,'jku'spoofing, oversize, clock-skew.

16) TL; DR

Фіксуйте алгоритм і ключі на стороні сервера, не довіряйте'alg'з токена, вимагайте'iss/aud/exp/nbf/iat/jti'. Ротуйте ключі через JWKS з перекриттям, тримайте токени короткоживучими, а refresh - одноразовими. Зберігайте токени безпечно (HttpOnly-cookie для веба), мінімізуйте клейми, не кладіть PII. Закривайте'jku/x5u/kid'-вектори, уникайте HS256 зі слабкими секретами, додавайте PoP/DPoP або mTLS на критичних шляхах і завжди робіть BOLA-перевірки на рівень ресурсу.

Contact

Зв’яжіться з нами

Звертайтеся з будь-яких питань або за підтримкою.Ми завжди готові допомогти!

Telegram
@Gamble_GC
Розпочати інтеграцію

Email — обов’язковий. Telegram або WhatsApp — за бажанням.

Ваше ім’я необов’язково
Email необов’язково
Тема необов’язково
Повідомлення необов’язково
Telegram необов’язково
@
Якщо ви вкажете Telegram — ми відповімо й там, додатково до Email.
WhatsApp необов’язково
Формат: +код країни та номер (наприклад, +380XXXXXXXXX).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.