JWT (JSON Web Token): плюсы, минусы и основные риски безопасности

Ответ

JWT — это открытый стандарт (RFC 7519) для создания токенов доступа, которые используются для безопасной передачи информации между сторонами в виде JSON-объекта. Токен состоит из трех частей: заголовок (Header), полезная нагрузка (Payload) и подпись (Signature).

Плюсы:

  • Stateless (отсутствие состояния на сервере): Серверу не нужно хранить информацию о сессиях. Вся необходимая информация (ID пользователя, роли) содержится в самом токене. Это упрощает архитектуру.
  • Масштабируемость: Благодаря stateless-подходу, запросы с одним и тем же токеном могут обрабатываться любым экземпляром сервиса, что идеально для микросервисной архитектуры и балансировки нагрузки.
  • Целостность данных: Подпись (Signature) гарантирует, что данные в токене не были изменены в пути. Сервер, зная секретный ключ, может проверить подлинность токена.
  • Кросс-доменность (CORS): JWT легко передавать в заголовках HTTP, что делает его удобным для взаимодействия между разными доменами и сервисами (например, веб-клиент на site.com и API на api.site.com).

Минусы и риски:

  • Невозможность принудительного отзыва: Главный недостаток. Если токен украден, он будет действителен до истечения своего срока (exp). Решение: использовать короткоживущие access-токены (5-15 минут) и долгоживущие refresh-токены для их обновления.
  • Размер: JWT обычно больше, чем ID сессии в cookie, так как содержит данные. Это может незначительно увеличить трафик, особенно при передаче с каждым запросом.
  • Безопасность хранения на клиенте:
    • localStorage: Уязвим для XSS-атак (любой скрипт на странице может получить доступ к токену).
    • Cookies: Более безопасный вариант, если использовать флаг HttpOnly (недоступен из JavaScript) и Secure (передача только по HTTPS).
  • Данные в Payload не зашифрованы: Содержимое полезной нагрузки (Payload) просто закодировано в Base64Url и легко читается кем угодно. Никогда не храните в JWT чувствительные данные (пароли, личную информацию)! Для шифрования используется другой стандарт — JWE (JSON Web Encryption).
// Пример создания JWT с использованием библиотеки github.com/golang-jwt/jwt
import (
    "time"
    "github.com/golang-jwt/jwt/v4"
)

// Секретный ключ должен храниться безопасно, например, в переменных окружения
var jwtKey = []byte("my_secret_key")

func createToken(userID int) (string, error) {
    // Устанавливаем срок жизни токена
    expirationTime := time.Now().Add(15 * time.Minute)

    // Создаем полезную нагрузку (claims)
    claims := &jwt.RegisteredClaims{
        Subject:   string(userID),
        ExpiresAt: jwt.NewNumericDate(expirationTime),
    }

    // Создаем токен с указанием алгоритма подписи и claims
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

    // Подписываем токен нашим секретным ключом
    return token.SignedString(jwtKey)
}