← Назад к вопросам

Почему JWT безопасен?

1.6 Junior🔥 181 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое JWT?

JWT (JSON Web Token) — это подписанный токен для безопасной передачи информации между клиентом и сервером. Структура JWT: Header.Payload.Signature

  1. Header (Base64) — алгоритм подписи (HS256, RS256)
  2. Payload (Base64) — данные пользователя (sub, name, exp)
  3. Signature (HMAC/RSA) — криптографическая подпись

Почему JWT безопасен?

1. Криптографическая подпись предотвращает подделку

JWT подписывается с использованием секретного ключа. Изменение любого бита в Header или Payload автоматически инвалидирует подпись:

import jwt
from datetime import datetime, timedelta

SECRET_KEY = 'super-secret-key'

# Создание токена
payload = {
    'sub': '502',
    'name': 'Alice',
    'exp': datetime.utcnow() + timedelta(hours=1)
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')

# Проверка подписи
decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])

# Попытка подделки: изменить name
forged_token = token[:-10] + 'XXXXXXXXXX'
try:
    jwt.decode(forged_token, SECRET_KEY, algorithms=['HS256'])
except jwt.InvalidSignatureError:
    print('Подпись недействительна!')

2. Невозможно подделать без секретного ключа

Хакер видит только Base64-encoded данные. Он может декодировать Header и Payload, но не может создать правильную подпись без SECRET_KEY.

3. Проверка осуществляется на сервере

Сервер имеет SECRET_KEY и может мгновенно проверить подлинность токена:

from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer
import jwt

app = FastAPI()
security = HTTPBearer()
SECRET_KEY = 'super-secret-key'

async def verify_token(credentials = Depends(security)):
    token = credentials.credentials
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        user_id = payload.get('sub')
        return user_id
    except jwt.InvalidSignatureError:
        raise HTTPException(status_code=401, detail='Invalid token')
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail='Token expired')

@app.get('/protected')
async def protected_route(user_id: str = Depends(verify_token)):
    return {'message': f'Hello, user {user_id}'}

4. Защита от обычных атак

Man-in-the-Middle (MITM): JWT должен передаваться по HTTPS, чтобы хакер не мог перехватить и переиспользовать токен.

Cross-Site Request Forgery (CSRF): JWT хранится в httpOnly cookie или заголовке Authorization. Это предотвращает доступ из JavaScript.

# Правильно: httpOnly cookie
response.set_cookie(
    'token',
    token,
    httponly=True,
    secure=True,
    samesite='Strict'
)

Асимметричная криптография (RS256)

Для более высокой безопасности используется RSA вместо HMAC:

import jwt
from cryptography.hazmat.primitives.asymmetric import rsa

# Генерация пары ключей
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

# Подпись с приватным ключом (только сервер)
payload = {'sub': '502', 'name': 'Alice'}
token = jwt.encode(payload, private_key, algorithm='RS256')

# Проверка с публичным ключом
decoded = jwt.decode(token, public_key, algorithms=['RS256'])

Потенциальные уязвимости JWT

Слабый SECRET_KEY

# Плохо
SECRET_KEY = '123456'

# Хорошо
import secrets
SECRET_KEY = secrets.token_urlsafe(32)

Отсутствие TTL

# Плохо: токен без срока действия
payload = {'sub': '502'}

# Хорошо: добавить exp
from datetime import datetime, timedelta
payload = {
    'sub': '502',
    'exp': datetime.utcnow() + timedelta(hours=1)
}

Хранение чувствительных данных

# Плохо
payload = {'user_id': 502, 'password': 'secret123'}

# Хорошо
payload = {'sub': '502', 'roles': ['user', 'admin']}

Забыть о Blacklist при logout

def logout(token: str):
    # Добавить токен в blacklist с TTL = оставшемуся времени
    r.setex(f'blacklist:{token}', 3600, '1')

Вывод

JWT безопасен по конструкции благодаря:

  1. Криптографической подписи — подделка очень сложна
  2. Невозможности подделать без секретного ключа
  3. Быстрой проверке на сервере
  4. Защите от типичных веб-атак (CSRF, session fixation)

Но нужно помнить:

  • Используй HTTPS
  • Установи разумный TTL
  • Используй сильный SECRET_KEY
  • Не храни чувствительные данные в payload
  • Реализуй blacklist при logout