Какие существуют способы передачи публичного ключа для верификации JWT?

«Какие существуют способы передачи публичного ключа для верификации JWT?» — вопрос из категории Безопасность, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Основной и рекомендуемый способ — использование JWKS (JSON Web Key Set). Это стандартный механизм, который позволяет клиентам динамически получать публичные ключи для проверки подписи JWT.

1. JWKS (JSON Web Key Set) — Стандартный подход

Это публичный эндпоинт (например, /.well-known/jwks.json), который возвращает JSON-объект с набором ключей. Такой подход позволяет серверу аутентификации ротировать ключи без необходимости вносить изменения в клиентские приложения.

Пример ответа JWKS:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "unique-key-id-1",
      "alg": "RS256",
      "n": "...base64url-encoded-modulus...",
      "e": "AQAB"
    }
  ]
}
  • kty: Тип ключа (например, RSA).
  • use: Назначение ключа (sig — для подписи).
  • kid: Уникальный идентификатор ключа. JWT в своем заголовке содержит kid, чтобы клиент знал, какой именно ключ из набора использовать для верификации.

2. Другие способы (менее гибкие)

  • Встраивание в конфигурацию клиента: Публичный ключ жестко прописывается в конфигурации клиентского приложения. Главный минус — при смене ключа на сервере требуется обновление всех клиентов.
  • Передача в заголовке JWT (jwk): Публичный ключ встраивается непосредственно в заголовок токена. Используется редко, так как значительно увеличивает размер токена.

Пример получения ключа из JWKS на Python:

import jwt
import requests

# URL эндпоинта с ключами
jwks_url = "https://auth.example.com/.well-known/jwks.json"

# Получаем JWT, который нужно проверить
token = "eyJhbGciOiJSUzI1NiIsImtpZCI6InVuaXF1ZS1rZXktaWQtMSJ9..."

# 1. Получаем kid из заголовка токена
header = jwt.get_unverified_header(token)
kid = header['kid']

# 2. Загружаем ключи с JWKS-эндпоинта
jwks_client = jwt.PyJWKClient(jwks_url)
signing_key = jwks_client.get_signing_key_from_jwt(token)

# 3. Верифицируем токен с помощью найденного ключа
data = jwt.decode(
    token, 
    signing_key.key, 
    algorithms=["RS256"], 
    options={"verify_aud": False}
)
print(data)