1. Анатомия гибридной авторизации: фундаментальные различия и роли токенов и кук
Анатомия гибридной авторизации: фундаментальные различия и роли токенов и кук
Представьте типичный сценарий тестирования банковского API: вы успешно получили access_token через эндпоинт авторизации, добавили его в заголовок Authorization: Bearer ... и отправили запрос на получение списка предодобренных продуктов. Вместо ожидаемого JSON с данными сервер возвращает 401 Unauthorized. Вы проверяете срок действия токена — он валиден. Вы проверяете права доступа — они в порядке. Но сервер непреклонен. Причина кроется в том, что в современных высокозащищенных системах, таких как ВТБ Онлайн, классическая схема OAuth2 часто работает не в одиночку, а в «гибридной» связке с сессионными механизмами.
В банковской среде безопасность строится по принципу эшелонированной обороны. Если в обычном интернет-магазине для идентификации пользователя достаточно одного лишь токена, то в финтехе этого критически мало. Здесь в игру вступает гибридная авторизация — архитектурный подход, объединяющий преимущества stateless-токенов (JWT) и stateful-сессий (Cookies). Для QA-инженера это означает, что объект тестирования усложняется: теперь недостаточно проверить один заголовок, нужно контролировать целостность всей связки «токен + куки + отпечаток устройства».
Две парадигмы: почему токена стало недостаточно
Чтобы понять, зачем банки усложняют жизнь разработчикам и тестировщикам, нужно разграничить два фундаментальных подхода к управлению состоянием пользователя: Stateful (с сохранением состояния) и Stateless (без сохранения состояния).
Исторически веб-приложения были полностью stateful. Сервер создавал в своей памяти или базе данных запись о сессии, а клиенту отдавал лишь идентификатор — Session ID в куках.
> Stateful подход
>
> Сервер несет «бремя памяти». Каждый запрос клиента должен содержать ID, по которому сервер найдет в своей базе объект сессии и поймет, кто к нему пришел. Если база сессий «упадет», все пользователи разлогинятся.
Затем наступила эра микросервисов и мобильных приложений, принесшая Stateless подход в лице OAuth2 и JWT (JSON Web Tokens). Здесь сервер не хранит информацию о сессии. Вся необходимая информация (ID пользователя, роли, срок действия) зашита в сам токен и подписана криптографическим ключом. Серверу достаточно проверить подпись, чтобы доверять данным внутри токена.
Однако в банковских API возник парадокс. Stateless-токены удобны для масштабирования, но их крайне сложно отозвать мгновенно. Если злоумышленник украдет JWT, он сможет пользоваться им до конца срока жизни токена (), так как сервер не проверяет состояние сессии в базе при каждом запросе. В банке, где транзакция может длиться секунды, а риск исчисляется миллионами, такая задержка недопустима.
Гибридная модель — это попытка взять лучшее от обоих миров.
Анатомия участников: Access Token vs Cookies
Давайте разберем «действующих лиц», которые вы видите в DevTools или Postman при работе с API ВТБ.
Access Token (Bearer)
Это ваш «пропуск» в конкретные комнаты здания. Технически это чаще всего JWT. Его главная характеристика — краткосрочность. В банковских системах время жизни access-токена может составлять от 5 до 15 минут.sessionStorage браузера.Authorization: Bearer <token>.token_expired.Refresh Token
Это «ключ от сейфа», в котором лежат новые пропуски. Он долгоживущий (от нескольких часов до нескольких дней)./oauth2/token с grant_type=refresh_token.Cookies: vtb_session_id и USER_FINGERPRINT
А вот здесь начинается специфика гибрида. Помимо токенов, сервер выставляет куки.vtb_session_id не соответствует тому, что привязан к этому токену в базе сессий (Session Store), запрос будет отклонен.> «Зачем нужен Fingerprint, если есть токен?» — частый вопрос на собеседованиях QA.
>
> Представьте, что злоумышленник перехватил ваш access_token и vtb_session_id. Он пытается отправить запрос со своего компьютера. Сервер видит: токен верный, сессия активна, но USER_FINGERPRINT другой (другой браузер или IP). Это сигнал для антифрод-системы немедленно терминировать сессию.
Механизм связки: как бэкенд понимает, что «одно принадлежит другому»
Ключевая проблема гибридной авторизации — Validation Coupling (связанность валидации). Когда вы отправляете запрос на GET /preregistration/list, на стороне API Gateway или специализированного Security-модуля происходит сложная проверка.
Математически это можно представить как функцию валидации , которая принимает набор параметров:
Где:
vtb_session_id;Если хотя бы один элемент не соответствует остальным в связке, результат будет .
Внутри бэкенда это работает так:
Subject ID (идентификатор пользователя) из JWT.vtb_session_id из кук и идет в Redis (или другое хранилище сессий).session_id == vtb_session_id.Subject ID?
- Совпадает ли fingerprint из базы с тем, что пришел в текущем запросе?
- Не была ли эта сессия принудительно закрыта (например, пользователь нажал «Выйти на всех устройствах»)?Почему вы получаете 401 Unauthorized при пустых куках? Потому что для банка наличие валидного JWT — это лишь необходимое, но не достаточное условие. Без куки сервер не может выполнить шаг №2. Для него вы — человек с ключом, который пытается открыть дверь, но при этом прячет лицо за маской и отказывается предъявить паспорт. В банковской логике отсутствие сессионного контекста при наличии токена приравнивается к попытке взлома или подмены запроса (Replay Attack).
Сравнение подходов в контексте тестирования
| Характеристика | Чистый OAuth2 (Stateless) | Гибридная модель (Stateful + Stateless) | | :--- | :--- | :--- | | Основной маркер | Access Token (JWT) | Access Token + Session Cookie | | Место хранения | Память приложения | Память + Cookie Jar | | Проверка на бэкенде | Только криптографическая подпись | Подпись + Поиск в базе сессий | | Мгновенный отзыв | Невозможен (до истечения TTL) | Возможен через удаление сессии в БД | | Сложность для QA | Низкая (нужен только заголовок) | Высокая (нужен менеджмент кук) |
Для QA-инженера гибридная модель означает, что состояние теста теперь распределено. Если раньше вы могли просто скопировать токен из одного запроса в другой в Postman и радоваться жизни, то теперь вам нужно эмулировать поведение реального браузера или мобильного клиента, который бережно хранит и переиспользует куки.
Ротация при обновлении: критическая точка отказа
Самый сложный момент в тестировании API ВТБ (и подобных систем) — это процедура refresh_token.
Когда вы вызываете POST /oauth2/token, происходит не просто замена токена. Сервер выполняет ротацию сессии.
vtb_session_id аннулируется.vtb_session_id.USER_FINGERPRINT (если в логику заложено обновление отпечатка при каждом обновлении ключей).Set-Cookie.Если ваш автотест обновил токен в переменной {{access_token}}, но не обновил куки в своем CookieJar, следующий же запрос к бизнес-методу (например, получение баланса) упадет. Сервер увидит новый токен, но старую куку. В его базе старая кука уже помечена как «невалидная» или «использованная», так как произошел Refresh. Для системы безопасности это выглядит как попытка использовать украденную сессию после того, как легитимный пользователь её уже обновил.
Нюансы для автоматизатора
При проектировании тестов на Java с использованием RestAssured, важно понимать, что стандартный фильтр RequestLoggingFilter может не показать вам всей правды о куках, если они обрабатываются автоматически.
Ключевые моменты, которые нужно учитывать:
HttpOnly. Это значит, что JavaScript (и ваш скрипт в Postman, если он не имеет специальных прав) не может прочитать их напрямую. Однако HTTP-клиент (RestAssured или движок Postman) обязан отправлять их обратно.SameSite=Strict. При тестировании кросс-доменных запросов (если API разнесено по разным поддоменам) это может стать причиной внезапных 401 ошибок.Гибридная авторизация — это компромисс между удобством разработки и жесткими требованиями безопасности. Понимая, что vtb_session_id — это не просто «какая-то кука», а вторая половина вашего токена, вы сможете гораздо быстрее локализовать дефекты, связанные с «внезапным» разлогином пользователей или ошибками 401 в автотестах. В следующих частях мы детально разберем, как именно бэкенд связывает эти сущности и как реализовать надежный механизм их обновления в коде.