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

Как происходит защита JWT токена от подделки

1.6 Junior🔥 251 комментариев
#Другое

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

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

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

Как происходит защита JWT токена от подделки

JWT (JSON Web Token) — это широко используемый стандарт аутентификации, который обеспечивает защиту от подделки благодаря **криптографической подписи**. Рассмотрим механизм защиты в деталях.

Структура JWT

JWT состоит из трёх частей, разделённых точками:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  1. Header (заголовок) — тип токена и алгоритм хеширования
  2. Payload (нагрузка) — данные пользователя (claims)
  3. Signature (подпись) — криптографическая подпись

Как создаётся подпись

Серверная сторона создаёт подпись по следующему алгоритму:

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;

public class JwtProvider {
    private final SecretKey secretKey;
    
    public JwtProvider(String secret) {
        this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
    }
    
    public String generateToken(String userId) {
        return Jwts.builder()
            .setSubject(userId)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 3600000))
            .signWith(secretKey, SignatureAlgorithm.HS256)
            .compact();
    }
}

Физически подпись вычисляется так:

SIGNATURE = HMAC-SHA256(
    secretKey,
    base64url(header) + "." + base64url(payload)
)

Целая точка: если кто-то попытается изменить header или payload, подпись станет неверной.

Верификация подписи на сервере

Когда клиент отправляет токен, сервер проверяет его подлинность:

public class JwtValidator {
    private final SecretKey secretKey;
    
    public JwtValidator(String secret) {
        this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
    }
    
    public String validateToken(String token) {
        try {
            return Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
        } catch (JwtException | IllegalArgumentException e) {
            throw new SecurityException("Invalid JWT token", e);
        }
    }
}

Процесс верификации:

  1. Парсинг — разделяем токен на header, payload и signature
  2. Воссоздание подписи — берём header + payload и пересчитываем HMAC с серверным secret key
  3. Сравнение — если пересчитанная подпись совпадает с полученной, токен подлинный
// Псевдокод верификации
String[] parts = token.split("\\.");
String header = parts[0];
String payload = parts[1];
String receivedSignature = parts[2];

// Пересчитываем подпись
String expectedSignature = HMAC_SHA256(
    secretKey,
    header + "." + payload
);

// Проверяем совпадение
if (!constantTimeEquals(receivedSignature, expectedSignature)) {
    throw new SecurityException("Invalid signature");
}

Почему подделка невозможна

  1. Нет секретного ключа — Злоумышленник может видеть header и payload (они только base64-encoded, не зашифрованы), но не может пересчитать подпись без secret key.

  2. HMAC необратимо — Функция HMAC-SHA256 — это односторонняя хеш-функция. Зная подпись, невозможно восстановить secret key.

  3. Защита от временных атак — Java JWT библиотеки (как JJWT) используют constantTimeEquals() для сравнения подписей, чтобы предотвратить timing attacks.

Алгоритмы подписей

АлгоритмТипБезопасностьИспользование
HS256HMAC + SHA-256Симметричный, быстроВнутренние сервисы
HS512HMAC + SHA-512Симметричный,強еКритичные данные
RS256RSA + SHA-256АсимметричныйМикросервисы, публичные API
ES256ECDSAАсимметричный, компактныйMobile, высокий throughput

Пример с асимметричным ключом (RS256)

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

import java.security.KeyPair;
import java.security.KeyPairGenerator;

public class JwtProviderRSA {
    private final PrivateKey privateKey;
    private final PublicKey publicKey;
    
    public JwtProviderRSA() throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048);
        KeyPair keyPair = keyGen.generateKeyPair();
        this.privateKey = keyPair.getPrivate();
        this.publicKey = keyPair.getPublic();
    }
    
    public String generateToken(String userId) {
        return Jwts.builder()
            .setSubject(userId)
            .signWith(privateKey, SignatureAlgorithm.RS256)
            .compact();
    }
    
    public String validateToken(String token) {
        return Jwts.parserBuilder()
            .setSigningKey(publicKey)
            .build()
            .parseClaimsJws(token)
            .getBody()
            .getSubject();
    }
}

Преимущество RS256: публичный ключ может быть распределён (например, другие сервисы могут валидировать токены), а приватный ключ остаётся защищённым.

Практические рекомендации

  1. Никогда не меняйте secret key — это сделает все существующие токены невалидными.
  2. Хранните secret key в environment переменных, не в коде.
  3. Используйте HTTPS — передавайте JWT только по защищённому каналу.
  4. Установите expiration — добавляйте setExpiration() для автоматического истечения токенов.
  5. Используйте RS256 для микросервисов — где разные сервисы должны валидировать токены без доступа к secret key.
  6. Проверяйте токен на каждом запросе — в фильтрах или интерсепторах.
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final JwtValidator jwtValidator;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        String token = extractToken(request);
        if (token != null) {
            try {
                String userId = jwtValidator.validateToken(token);
                // Установить authentication в SecurityContext
            } catch (SecurityException e) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }
}

Таким образом, JWT обеспечивает криптографическую гарантию подлинности благодаря HMAC или RSA подписи. Без знания secret key (или приватного ключа) невозможно создать валидный подделанный токен.

Как происходит защита JWT токена от подделки | PrepBro