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

Почему токены обычно используют два ключа?

2.0 Middle🔥 131 комментариев
#Безопасность

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Почему используется пара ключей: приватный и публичный?

В основе использования двух ключей (приватного и публичного) лежит принцип асимметричной криптографии, которая кардинально отличается от симметричной. Это не просто "два ключа", а математически связанная пара, где каждый ключ выполняет строго отведённую роль. Давайте разберем, почему эта модель стала доминирующей для токенов (JWT, доступ к API) и безопасной передачи данных.

Основная причина: разделение ответственности и безопасное распространение верификатора

Главный философский и технический ответ — секретная информация (приватный ключ) никогда не покидает доверенную среду (issuer), в то время как публичная часть, которая не является секретом, может свободно распространяться среди всех, кто должен проверять подлинность (verifiers).

  • Приватный ключ (Private Key): Это секрет. Он известен только владельцу (серверу, выпускающему токены — authorization server). Он используется для подписи (signing) токена. Любой, кто обладает этим ключом, может создавать действительные токены, поэтому его хранят с максимальной безопасностью (в аппаратных модулях безопасности — HSM, секретах Kubernetes, vault).
  • Публичный ключ (Public Key): Это не секрет. Его можно публиковать в открытых JWKS (JSON Web Key Set) эндпоинтах, встраивать в клиенты, конфигурации микросервисов. Он используется только для верификации (verifying) подписи. Обладание публичным ключом не позволяет подделать токен, а только проверить, что он был подписан соответствующим приватным ключом.

Конкретные преимущества двухключевой модели (RSA, ECDSA)

  1. Устранение проблемы распределения общего секрета (решаемая симметричными алгоритмами, вроде HS256):
    *   При использовании одного ключа (HMAC) и сервер выдачи, и все сервисы-проверяющие должны знать один и тот же секрет. Распределение и ротация этого секрета между десятками микросервисов становится серьезной проблемой безопасности. При компрометации одного сервиса секрет скомпрометирован везде.
    *   **Асимметричная схема** решает это: компрометация публичного ключа (например, на frontend-клиенте) не дает атакующему возможности создавать токены. Секрет (приватный ключ) остается в одном, максимально защищенном месте.

  1. Неотрекаемость (Non-repudiation):
    *   Если токен подписан приватным ключом, который принадлежит исключительно серверу авторизации, этот сервер не может впоследствии отрицать факт выпуска данного токена. Это важно для аудита и соответствия стандартам.

  1. Безопасная публичная верификация:
    *   Публичные ключи можно свободно кэшировать на стороне проверяющих (API Gateway, микросервисы). Их не нужно часто менять. Ротация ключей происходит путем публикации нового ключа в JWKS, при этом старые токены, выпущенные старым ключом, могут продолжать валидироваться до истечения своего срока, если старый публичный ключ еще доступен.

  1. Поддержка сложных федеративных сценариев:
    *   В схемах OAuth 2.0 / OpenID Connect (OIDC) Identity Provider (IdP, например, Auth0, Keycloak, Azure AD) публикует свои публичные ключи по известному адресу `.well-known/jwks.json`. Ваше приложение (Relying Party), не имея с IdP никакого предварительно разделенного секрета, может уверенно проверять токены, просто загрузив его публичные ключи по HTTPS. Это основа доверия в интернете.

Техническая иллюстрация: подпись и проверка JWT

Рассмотрим на примере JWT, подписанного по алгоритму RS256 (RSA Signature with SHA-256).

Шаг 1: Создание токена (на стороне Authorization Server) Authorization Server генерирует подпись для заголовка и полезной нагрузки (payload), используя свой приватный ключ.

// Псевдокод для процесса подписания
const crypto = require('crypto');
const header = base64UrlEncode({ "alg": "RS256", "typ": "JWT" });
const payload = base64UrlEncode({ "sub": "user123", "exp": 1735689600 });
const dataToSign = `${header}.${payload}`;

// Приватный ключ никогда не покидает эту защищенную среду
const privateKey = getSecurePrivateKey();
const signature = crypto.sign('sha256', Buffer.from(dataToSign), {
    key: privateKey,
    padding: crypto.constants.RSA_PKCS1_PSS_PADDING
});
const jwt = `${dataToSign}.${base64UrlEncode(signature)}`;
// jwt отправляется клиенту

Шаг 2: Проверка токена (на стороне любого API Gateway или микросервиса) Проверяющая сторона получает публичный ключ от Authorization Server (например, из кэша) и использует его для верификации.

# Псевдокод для процесса проверки (например, на Python)
import jwt
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat

# Публичный ключ, свободно полученный из JWKS
public_key_pem = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxyz... (ключ из JWKS)
-----END PUBLIC KEY-----"""

# Верификация токена, полученного от клиента
def verify_access_token(token: str):
    try:
        # Алгоритм указывается явно для предотвращения атак на понижение алгоритма
        decoded_payload = jwt.decode(
            token,
            public_key_pem,
            algorithms=["RS256"],  # Явно указываем ожидаемый алгоритм
            options={"verify_exp": True}
        )
        # Если подпись невалидна, будет выброшено исключение jwt.InvalidSignatureError
        return decoded_payload  # Токен и подпись верны
    except jwt.exceptions.InvalidSignatureError:
        # Сигнал о возможной подделке
        return None

Эволюция и современный контекст

Сегодня в экосистеме OIDC и микросервисов использование асимметричных пар ключей (чаще всего на эллиптических кривых — ES256, как более безопасная и эффективная альтернатива RSA) является стандартом де-факто. Это позволяет:

  • Сервисам аутентификации (AuthN) быть отдельными от сервисов авторизации (AuthZ).
  • Легко реализовывать ротацию ключей без простоя сервисов.
  • Строить федерацию идентификации между разными доверенными доменами.

Итог: Два ключа используются для создания криптографически безопасной, масштабируемой и практичной системы доверия, где секретная часть никогда не распространяется, а возможность проверки подлинности — доступна всем легитимным участникам системы. Это краеугольный камень безопасности современных распределенных приложений.