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

Можно ли проверить только подпись при валидации JSON Web Token?

2.0 Middle🔥 191 комментариев
#Другое

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

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

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

Валидация подписи JSON Web Token

Да, можно проверить только подпись при валидации JWT, но это опасный и неправильный подход в production среде. Опытный разработчик должен понимать почему, и всегда проверять все компоненты токена.

Структура JSON Web Token

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

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

Технически можно, но практически нельзя

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;

public class JwtValidator {
    
    // ПЛОХОЙ ПРИМЕР: проверка ТОЛЬКО подписи
    public boolean validateJwtSignatureOnly(String token, String secret) {
        try {
            Jwts.parserBuilder()
                .setSigningKey(
                    Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8))
                )
                .build()
                .parseClaimsJws(token);
            return true; // Подпись верна
        } catch (Exception e) {
            return false; // Подпись неверна
        }
    }
}

Технически это работает, но не проверяет критические параметры.

Почему проверка только подписи опасна

1. Не проверяется истечение срока (exp)

public class SecurityFlaw {
    
    // Токен давно истёк, но подпись ещё действительна!
    public void vulnerableEndpoint(String token, String secret) {
        if (validateJwtSignatureOnly(token, secret)) {
            // УЯЗВИМОСТЬ: старый токен всё ещё принимается
            // Пользователь мог быть заблокирован
            processRequest();
        }
    }
}

2. Не проверяется аудитория (aud)

public class AudienceVulnerability {
    
    // Если не проверить aud, токен можно использовать в других сервисах
    public void loginService(String token, String secret) {
        if (validateJwtSignatureOnly(token, secret)) {
            // Токен, выданный для API, используется в web-приложении
            processLogin();
        }
    }
}

3. Не проверяется издатель (iss)

public class IssuerVulnerability {
    
    // Токен от другого провайдера может быть принят
    public boolean isValidToken(String token, String secret) {
        // Проверка подписи может пройти, если у них одинаковый secret
        return validateJwtSignatureOnly(token, secret);
    }
}

Правильная валидация JWT

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.util.Date;

public class SecureJwtValidator {
    private static final String ISSUER = "my-app";
    private static final String AUDIENCE = "my-api";
    
    public Claims validateJwtFully(String token, String secret) {
        try {
            Claims claims = Jwts.parserBuilder()
                .setSigningKey(
                    Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8))
                )
                .requireIssuer(ISSUER)        // Проверить издателя
                .requireAudience(AUDIENCE)    // Проверить аудиторию
                .build()
                .parseClaimsJws(token)
                .getBody();
            
            // Дополнительная проверка истечения (обычно встроена)
            if (claims.getExpiration() != null) {
                if (claims.getExpiration().before(new Date())) {
                    throw new IllegalArgumentException("Токен истёк");
                }
            }
            
            return claims;
        } catch (Exception e) {
            throw new SecurityException("Токен невалиден: " + e.getMessage(), e);
        }
    }
}

Пример JWT с полным контролем

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.util.Date;

public class JwtTokenGenerator {
    
    public String generateToken(String userId, String secret) {
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        Date expirationDate = new Date(nowMillis + 3600000); // 1 час
        
        return Jwts.builder()
            .setSubject(userId)
            .setIssuedAt(now)
            .setExpiration(expirationDate)
            .claim("iss", "my-app")           // Издатель
            .claim("aud", "my-api")           // Аудитория
            .claim("role", "user")            // Роль
            .signWith(
                Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)),
                SignatureAlgorithm.HS256
            )
            .compact();
    }
}

Полный цикл валидации в Spring Security

import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.stereotype.Component;

@Component
public class JwtAuthenticationValidator {
    
    private final JwtDecoder jwtDecoder;
    
    public JwtAuthenticationValidator(JwtDecoder jwtDecoder) {
        this.jwtDecoder = jwtDecoder;
    }
    
    public Authentication validateAndGetAuthentication(String token) {
        try {
            // JwtDecoder автоматически проверяет:
            // 1. Подпись
            // 2. Expiration (exp)
            // 3. Issued At (iat)
            // 4. Not Before (nbf)
            Jwt jwt = jwtDecoder.decode(token);
            
            // Дополнительные проверки
            String issuer = jwt.getIssuer().toString();
            String audience = jwt.getAudience().toString();
            
            if (!issuer.equals("https://auth-server.example.com")) {
                throw new SecurityException("Неправильный издатель");
            }
            
            if (!audience.contains("my-api")) {
                throw new SecurityException("Неправильная аудитория");
            }
            
            // Вернуть авторизацию
            return createAuthentication(jwt);
        } catch (Exception e) {
            throw new SecurityException("Валидация токена не пройдена", e);
        }
    }
    
    private Authentication createAuthentication(Jwt jwt) {
        // Извлечь роли и создать Authentication
        // ...
        return null;
    }
}

Конфигурация Spring Security для JWT

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;

@Configuration
public class SecurityConfiguration {
    
    @Bean
    public JwtDecoder jwtDecoder() {
        NimbusJwtDecoder decoder = NimbusJwtDecoder
            .withJwkSetUri("https://auth-server.example.com/.well-known/jwks.json")
            .build();
        
        // Требовать проверку издателя
        decoder.getJwtClaimsSetVerifier().setRequireAudience(true);
        
        return decoder;
    }
}

Типичные ошибки при валидации JWT

Ошибка 1: Игнорирование expiration

// ПЛОХО
if (isSignatureValid(token)) {
    // Не проверяется exp!
    return true;
}

Ошибка 2: Жёсткое кодирование secret в коде

// ПЛОХО
private static final String SECRET = "my-super-secret-key";

// ХОРОШО
private final String secret; // Из конфигурации/environment

Ошибка 3: Не проверять kid (Key ID)

// Если используются несколько ключей (key rotation)
public String getSigningKeyForAlgorithm(String kid) {
    // Найти правильный ключ по ID
    return signingKeys.get(kid);
}

Проверочный список валидации JWT

  1. Подпись — проверяется алгоритм и ключ
  2. Expiration (exp) — токен не должен быть истёкшим
  3. Issued At (iat) — убедиться, что токен выдан в разумное время
  4. Not Before (nbf) — токен не должен использоваться раньше этого времени
  5. Issuer (iss) — токен от доверенного издателя
  6. Audience (aud) — токен предназначен для нашего сервиса
  7. Subject (sub) — пользователь существует в системе
  8. Custom claims — проверить роли, разрешения и т.д.

Заключение

Прямой ответ: Да, технически можно проверить только подпись JWT. Однако в production это критическая уязвимость безопасности.

Опытный Java разработчик всегда:

  • Проверяет все обязательные поля JWT
  • Использует готовые библиотеки (io.jsonwebtoken, Spring Security)
  • Понимает последствия каждого параметра
  • Реализует проверку expiration, issuer и audience
  • Использует современные стандарты OAuth 2.0 / OpenID Connect

Это фундаментальная компетенция для разработки безопасных приложений.

Можно ли проверить только подпись при валидации JSON Web Token? | PrepBro