← Назад к вопросам
Каким образом Backend определяет поддельный JWT?
2.0 Middle🔥 151 комментариев
#REST API и HTTP#Безопасность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Каким образом Backend определяет поддельный JWT?
JWT (JSON Web Token) — это подписанный токен, и его подлинность определяется через криптографическую проверку подписи.
1. Основной механизм: криптографическая подпись
JWT состоит из 3 частей: header.payload.signature
import jwt
class JWTValidator:
def __init__(self, secret_key: str):
self.secret_key = secret_key
def verify_jwt(self, token: str) -> dict:
try:
payload = jwt.decode(
token,
self.secret_key,
algorithms=['HS256']
)
return payload
except jwt.InvalidSignatureError:
raise ValueError("Invalid JWT signature - FAKE TOKEN")
except jwt.ExpiredSignatureError:
raise ValueError("JWT expired")
validator = JWTValidator("my-secret-key")
payload = validator.verify_jwt("eyJhbGc...")
Когда backend получает JWT:
- Расчитываем ожидаемую подпись HMAC(header + payload, secret)
- Сравниваем с полученной подписью
- Если не совпадает → поддельный JWT
2. Как подпись работает
import hmac
import hashlib
import json
import base64
def decode_jwt_manually(token: str, secret: str) -> bool:
header_b64, payload_b64, signature_b64 = token.split('.')
# Ожидаемая подпись
message = f"{header_b64}.{payload_b64}".encode('utf-8')
expected_signature = hmac.new(
secret.encode('utf-8'),
message,
hashlib.sha256
).digest()
# Полученная подпись
signature_b64 += '=' * (4 - len(signature_b64) % 4)
received_signature = base64.urlsafe_b64decode(signature_b64)
# Сравнение
is_valid = hmac.compare_digest(expected_signature, received_signature)
return is_valid
3. Частые атаки и защита
Атака 1: Algorithm confusion
# ❌ УЯЗВИМО
payload = jwt.decode(token, secret_key) # Без algorithms!
# ✅ ЗАЩИТА
payload = jwt.decode(
token,
secret_key,
algorithms=['HS256'] # Жёсткое ограничение!
)
Атака 2: Слабый secret
# ❌ УЯЗВИМО
secret = "password123" # Легко угадать!
# ✅ ЗАЩИТА
import secrets
secret = secrets.token_urlsafe(32)
4. Полная проверка JWT
from datetime import datetime, timedelta
class ComprehensiveJWTValidator:
def __init__(self, secret_key: str):
self.secret_key = secret_key
self.blacklist = set() # Для revoked tokens
def verify_jwt_comprehensive(self, token: str) -> dict:
try:
# 1. Проверяем подпись
payload = jwt.decode(
token,
self.secret_key,
algorithms=['HS256']
)
# 2. Проверяем expiration
if 'exp' in payload:
exp_timestamp = payload['exp']
if exp_timestamp < datetime.utcnow().timestamp():
raise ValueError("Token expired")
# 3. Проверяем not-before (iat)
if 'iat' in payload:
iat_timestamp = payload['iat']
if iat_timestamp > datetime.utcnow().timestamp():
raise ValueError("Token not yet valid")
# 4. Проверяем blacklist (revocation)
if token in self.blacklist:
raise ValueError("Token has been revoked")
# 5. Проверяем обязательные поля
required_fields = ['user_id', 'exp', 'iat']
if not all(field in payload for field in required_fields):
raise ValueError("Missing required fields")
return payload
except jwt.InvalidSignatureError:
raise ValueError("JWT signature is invalid")
def create_jwt(self, user_id: int, expires_in_hours: int = 24) -> str:
now = datetime.utcnow()
payload = {
'user_id': user_id,
'iat': int(now.timestamp()),
'exp': int((now + timedelta(hours=expires_in_hours)).timestamp())
}
return jwt.encode(payload, self.secret_key, algorithm='HS256')
5. Использование в FastAPI
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthCredentials
app = FastAPI()
security = HTTPBearer()
jwt_validator = ComprehensiveJWTValidator("secret-key")
def verify_token(credentials: HTTPAuthCredentials) -> dict:
try:
return jwt_validator.verify_jwt_comprehensive(credentials.credentials)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=str(e)
)
@app.post("/login")
async def login(username: str, password: str):
# Проверяем username и password
token = jwt_validator.create_jwt(user_id=123)
return {"access_token": token, "token_type": "bearer"}
@app.get("/protected")
async def protected_route(payload: dict = Depends(verify_token)):
return {"message": f"Hello, user {payload['user_id']}"}
6. Ключевые моменты
Как определяется поддельный JWT:
- ❌ Подпись не совпадает с ожидаемой
- ❌ Токен истёк (exp < now)
- ❌ Токен в чёрном списке (revoked)
- ❌ Отсутствуют обязательные поля
- ❌ Алгоритм не из допустимых
Защита:
- ✅ Секретный ключ > 32 символов
- ✅ Явно указываем algorithms=['HS256']
- ✅ Проверяем exp, iat, blacklist
- ✅ Используем HTTPS (не HTTP)
- ✅ Не хранишь sensitive данные в payload
Итог: Backend определяет поддельный JWT через криптографическую проверку подписи. Если подпись не совпадает — JWT поддельный. Дополнительно проверяются expiration, blacklist, обязательные поля и типы данных.