Токены с привязкой к отправителю — Versola Docs
/docs
До 1.00.7.5versola.ioGitHub

Токены с привязкой к отправителю

Как в Versola работают DPoP и access-токены, привязанные к сертификату mTLS, какие поля и ключи конфигурации ими управляют и как их проверяет edge.

Токен доступа с привязкой к отправителю связан с ключом или сертификатом, которым владеет клиент. Тот, кто скопировал такой токен, не сможет им воспользоваться без этого ключа или сертификата. Versola поддерживает два механизма: DPoP (RFC 9449) и токены, привязанные к сертификату mTLS (RFC 8705).

Чем опасны bearer-токены

Bearer-токен работает у любого, кто его предъявил. Если он утек из лога, прокси, расширения браузера или отчета о падении, владелец утечки может вызывать ваш API до истечения срока токена. Привязка к ключу меняет доказательство: вместо «у меня есть строка» нужно «у меня есть строка и закрытый ключ».

Тенанты с профилем безопасности fapi2 требуют от каждого клиента один из двух механизмов.

DPoP

В DPoP клиент владеет парой ключей и подписывает для каждого запроса короткий JWT-proof.

Что отправляет клиент:

ЗапросЗаголовки
Запрос токена в authDPoP: <proof>. Access-токен в ответе привязан к ключу proof как cnf.jkt
Вызов API через edgeAuthorization: DPoP <access_token> и DPoP: <proof>. В proof есть htm, htu и ath (хеш access-токена)

Как это проверяется:

  • Алгоритмы. По умолчанию ES256 и PS256. RS256 поддерживается, но в набор по умолчанию не входит. Ключи RSA — не короче 2048 бит.
  • htu. auth строит ожидаемый URL из настроенного issuer, а edge — из edge-url. Заголовок Host не используется. На listener с mTLS-алиасами htu должен совпадать с адресом алиаса.
  • Повтор. jti proof запоминается, повторное использование отклоняется.
  • Nonce. Если nonce обязателен, proof без свежего nonce получает ответ use_dpop_nonce с заголовком DPoP-Nonce. Клиент повторяет запрос, добавив nonce в proof. auth требует nonce на уровне тенанта (requireDpopNonce, по умолчанию false; Security, Tokens & keys, DPoP). edge требует его, когда у записи в Edges включено Require DPoP nonce.
  • Где. auth проверяет proof на /token и /userinfo. edge проверяет его на проксируемых вызовах.

Ключи конфигурации:

ГдеКлючПо умолчаниюСмысл
authdpop.iat-leeway60sДопустимое расстояние между iat proof и текущим временем
authdpop.nonce-ttl300sСколько времени выданный nonce принимается
edgedpop.iat-leewayзадается в конфигурации edgeТо же на прокси
edgedpop.nonce-ttlзадается в конфигурации edgeТо же на прокси
edgedpop.nonce-saltзадается в конфигурации edgeСекрет для генерации nonce

Поля клиента (Central API, Clients в консоли):

ПолеСмысл
dpopBoundAccessTokensКлиент всегда использует DPoP. Запрос токена без proof отклоняется с invalid_dpop_proof
dpopSigningAlgsСужает алгоритмы proof для клиента. Пустой список ничего не сужает. Можно только убрать алгоритмы, которые сервер объявляет в dpop_signing_alg_values_supported
dpopMinRsaKeySizeМинимальная длина модуля RSA в битах. Значения меньше 2048 отклоняются при регистрации

Токены, привязанные к сертификату mTLS

В mTLS клиент аутентифицируется сертификатом X.509, а access-токен содержит SHA-256-отпечаток сертификата как cnf.x5t#S256. Introspection возвращает тот же claim.

  • Listener. auth дублирует /token, /introspect, /revoke, /par и /userinfo на отдельном TLS-listener на порту MPORT (по умолчанию 8083), который требует клиентский сертификат. Listener запускается, только если в конфигурации auth есть блок mutual-tls с certificate, private-key, trusted-certificates и external-url. У trusted-certificates нет значения по умолчанию: без него не доверялся бы ни один сертификат.
  • Discovery. Когда listener включен, discovery-документ получает mtls_endpoint_aliases с адресом из external-url. Для запросов с сертификатом клиент должен обращаться к этим алиасам.
  • mTLS на прокси. Если mTLS завершает обратный прокси, auth читает сертификат из заголовка. Настройка выполняется на уровне тенанта на странице Security, Tokens & keys, Mutual TLS: mtlsCertificateHeader и mtlsCertificateEncoding (urlEncodedPem или base64Der). Оба поля задаются или очищаются вместе. Клиент с аутентификацией по сертификату на тенанте без этой настройки отклоняется.

Поля клиента:

ПолеСмысл
authMethodtls_client_auth (субъект сертификата сверяется с зарегистрированным значением) или self_signed_tls_client_auth (ключ сертификата сверяется с jwks клиента). Не меняется после создания
mtlsAuthДля tls_client_auth: subjectType (subject_dn, san_dns, san_uri, san_ip, san_email) и subjectValue
certificateBoundAccessTokensПривязывает токены к сертификату для клиента, который аутентифицируется иначе. Клиент с аутентификацией по сертификату всегда получает привязанные токены

Исключение: у нативного клиента за edge сертификат принадлежит edge и общий для всех установок, поэтому его токены привязаны только к ключу DPoP устройства, а certificateBoundAccessTokens должно быть false.

Как выбрать

СитуацияЧто использовать
Мобильное или десктопное приложениеDPoP. Ключ хранится в хранилище ключей устройства
Сервис вызывает ваш API с контролируемого вами бэкендаtls_client_auth с токенами, привязанными к сертификату, или private_key_jwt с DPoP
Сервис или банк вызывает API за edgeТокен, привязанный к сертификату, на mutual-TLS listener edge или DPoP на обычном порту. См. how-to
Браузерное приложение за edgeНа стороне клиента настраивать нечего. Токены остаются внутри edge, браузер работает с сессией в cookie EDGE_SESSION
Нельзя запустить listener с клиентскими сертификатами или проксиDPoP

Как с ними работает edge

edge решает по claim cnf токена, а не по схеме, которую выбрал вызывающий.

ТокенКак предъявленРезультат
cnf.jktAuthorization: DPoP с корректным proofПринят
cnf.jktAuthorization: BearerОтклонен (dpop_downgrade). Это действует даже без блока dpop в edge
cnf.x5t#S256cookie EDGE_SESSIONПринят. Сертификат принадлежит самому edge
cnf.x5t#S256Authorization: Bearer на mutual-TLS listener, отпечаток сертификата равен claimПринят
cnf.x5t#S256Authorization: Bearer на mutual-TLS listener, другой сертификатОтклонен (certificate_bound_token_outside_session)
cnf.x5t#S256заголовок Authorization на обычном портуОтклонен (certificate_bound_token_outside_session). Сертификат вызывающего edge там не видит
cnf.jkt и cnf.x5t#S256proof DPoPОтклонен. Корректного proof недостаточно, если вторую половину привязки (сертификат) показать нельзя
без cnfAuthorization: DPoPОтклонен. Proof ничего не говорит о непривязанном токене

Если убрать блок dpop из конфигурации edge, проверка DPoP отключается, но токены, привязанные к ключу, по Bearer все равно отклоняются. Нативному приложению, работающему через edge, блок dpop обязателен.

Токен, привязанный к сертификату, от вызывающего принимается, только если edge запускает mutual-TLS listener (блок mutual-tls, MPORT), который читает сертификат из handshake. Обычный порт по-прежнему такой токен отклоняет. Настройка описана на странице Вызов API с токеном, привязанным к сертификату.

Как auth относится к edge на /userinfo

Если у эндпоинта включен fetchUserInfo, edge вызывает /userinfo в auth с токеном вызывающего. auth проверяет привязку токена там так же строго, как edge, а edge не может передать ни DPoP-proof вызывающего (он адресован edge), ни его сертификат. Поэтому edge сообщает, что владельца он уже проверил, и сообщает это своим собственным сертификатом на mutual-TLS listener’е auth (native.auth-mutual-tls-url), а не заголовком.

auth принимает такой вызов как вызов edge и пропускает для него, и только для него, проверки DPoP и сертификата владельца, если сертификат TLS-сессии:

  1. называет ровно один edge, как URI subject alternative name urn:versola:edge:<id>,
  2. подписан сертификатом из mutual-tls.edge-trusted-certificates в auth (это УЦ, действующие сейчас, проверяются при запуске) и это листовой сертификат, а не сертификат УЦ,
  3. принадлежит edge, который central по-прежнему перечисляет (auth читает id edge и назначенные им тенанты вместе с остальной конфигурацией), и
  4. принадлежит edge, назначенному тенанту клиента, которому выдан токен.

Если любое условие не выполнено, вызов оценивается как вызов любого клиента.

Одно имя ничего не доказывает: тот, кто выпускает клиентские сертификаты, может вписать в подписанный им сертификат любое имя. Поэтому издатель должен быть отдельным УЦ, который выпускает сертификаты edge и ничего больше, а edge-trusted-certificates должны входить и в trusted-certificates listener’а, чтобы handshake проходил. Если edge-trusted-certificates не задан, ни один сертификат не считается сертификатом edge, и /userinfo работает как для любого вызывающего.

Что проверяется для такого вызова по-прежнему: подпись токена, его отзыв и scope openid. Токен, привязанный и к ключу, и к сертификату, отклоняется (invalid_token) и в этом случае. Сертификат в пробрасываемом заголовке никогда не считается сертификатом edge. К остальным вызывающим, включая всех клиентов, применяются полные проверки привязки.

Прежний механизм, короткоживущее edge-assertion в заголовке Versola-Edge-Assertion, удален. central по-прежнему отдает auth реестр edge, но только с id и назначенными тенантами, без ключей.

Безопасность. Закрытый ключ сертификата edge — самый чувствительный ключ развертывания. Тот, кто им владеет, может вызывать /userinfo как этот edge для любого токена тенантов, назначенных этому edge, без доказательства владения: таков радиус поражения украденного сертификата edge. Что его ограничивает:

  • УЦ edge должен быть отдельным, никогда не УЦ ваших клиентов. Если edge-trusted-certificates называет УЦ, который выпускает и клиентские сертификаты (docker-local или бэкенд client-certificates с общим УЦ), то клиенту не дает предъявить имя edge только отказ central вписывать имена urn:versola:edge: в клиентские сертификаты. auth пишет предупреждение при запуске, если listener больше никому не доверяет.
  • Короткие сроки. Срок самого сертификата после выпуска сократить нельзя. Пакет vps выпускает сертификаты edge на 30 дней, edge продлевает их.
  • Удаление edge в central или снятие назначения тенантов отрезает его в auth после следующего обновления конфигурации (configuration-cache-refresh-interval в auth.conf: одна минута в docker-local, пять в остальных), а не по истечении срока. Если central недоступен, auth сохраняет последний полученный реестр; если ни одного не загружено, ни один сертификат не считается сертификатом edge.
  • Открывайте mutual-TLS listener auth только из сети, в которой работают edge, и ротируйте УЦ edge как любой другой.

Настройка на стороне auth — в разделе mutual-tls. О токене, который работает за edge, см. DPoP или токены, привязанные к сертификату.

См. также