Токен доступа с привязкой к отправителю связан с ключом или сертификатом, которым владеет клиент. Тот, кто скопировал такой токен, не сможет им воспользоваться без этого ключа или сертификата. Versola поддерживает два механизма: DPoP (RFC 9449) и токены, привязанные к сертификату mTLS (RFC 8705).
Чем опасны bearer-токены
Bearer-токен работает у любого, кто его предъявил. Если он утек из лога, прокси, расширения браузера или отчета о падении, владелец утечки может вызывать ваш API до истечения срока токена. Привязка к ключу меняет доказательство: вместо «у меня есть строка» нужно «у меня есть строка и закрытый ключ».
Тенанты с профилем безопасности fapi2 требуют от каждого клиента один из двух механизмов.
DPoP
В DPoP клиент владеет парой ключей и подписывает для каждого запроса короткий JWT-proof.
Что отправляет клиент:
| Запрос | Заголовки |
|---|---|
Запрос токена в auth | DPoP: <proof>. Access-токен в ответе привязан к ключу proof как cnf.jkt |
Вызов API через edge | Authorization: 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должен совпадать с адресом алиаса.- Повтор.
jtiproof запоминается, повторное использование отклоняется. - 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проверяет его на проксируемых вызовах.
Ключи конфигурации:
| Где | Ключ | По умолчанию | Смысл |
|---|---|---|---|
auth | dpop.iat-leeway | 60s | Допустимое расстояние между iat proof и текущим временем |
auth | dpop.nonce-ttl | 300s | Сколько времени выданный nonce принимается |
edge | dpop.iat-leeway | задается в конфигурации edge | То же на прокси |
edge | dpop.nonce-ttl | задается в конфигурации edge | То же на прокси |
edge | dpop.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). Оба поля задаются или очищаются вместе. Клиент с аутентификацией по сертификату на тенанте без этой настройки отклоняется.
Поля клиента:
| Поле | Смысл |
|---|---|
authMethod | tls_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.jkt | Authorization: DPoP с корректным proof | Принят |
cnf.jkt | Authorization: Bearer | Отклонен (dpop_downgrade). Это действует даже без блока dpop в edge |
cnf.x5t#S256 | cookie EDGE_SESSION | Принят. Сертификат принадлежит самому edge |
cnf.x5t#S256 | Authorization: Bearer на mutual-TLS listener, отпечаток сертификата равен claim | Принят |
cnf.x5t#S256 | Authorization: Bearer на mutual-TLS listener, другой сертификат | Отклонен (certificate_bound_token_outside_session) |
cnf.x5t#S256 | заголовок Authorization на обычном порту | Отклонен (certificate_bound_token_outside_session). Сертификат вызывающего edge там не видит |
cnf.jkt и cnf.x5t#S256 | proof DPoP | Отклонен. Корректного proof недостаточно, если вторую половину привязки (сертификат) показать нельзя |
без cnf | Authorization: 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-сессии:
- называет ровно один edge, как URI subject alternative name
urn:versola:edge:<id>, - подписан сертификатом из
mutual-tls.edge-trusted-certificatesвauth(это УЦ, действующие сейчас, проверяются при запуске) и это листовой сертификат, а не сертификат УЦ, - принадлежит edge, который
centralпо-прежнему перечисляет (authчитает id edge и назначенные им тенанты вместе с остальной конфигурацией), и - принадлежит 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 или токены, привязанные к сертификату.