JWT-токен в cookie в ASP.Net Core: максимальная безопасность и контроль
В этом материале мы подробно рассмотрим механизм передачи JWT-токенов (JSON Web Token) через HTTP-Only cookie, который выводит безопасность и контроль над пользовательской сессией на новый уровень
325 открытий2К показов
JWT-токены распространены, чтобы обеспечивать процессы авторизации и аутентификации между сервером и клиентами. Стандартная схема их использования включает в себя передачу токена через заголовок Authorization и обновление с использованием рефреш-токена и рефреш-эндпоинта.
Во время аутентификации клиент получает от сервера токен и должен его где-то сохранить, чтобы потом передавать серверу с каждым запросом. Хранится токен в месте, доступном из JS кода, что порождает уязвимость к XSS атакам. Кроме того, клиенту требуется реализовать логику рефреша.
Иногда в проекте может возникнуть потребность в полном контроле над токеном со стороны сервера: он должен иметь возможность не только проверить, но и обновить или отозвать токен. В этом случае можно применять передачу JWT-токенов через HTTP-Only cookie. Об этом — техлид IT_ONE Сергей Белоусов.
Настройка контейнера ServiceCollection
Рассмотрим на примере. Для добавления работы с JWT-токенами через cookie в фреймфорке ASP.NET Core необходимо в Program.cs добавить следующие строки
Код контроллера для запуска и защиты сессии
Разберём подробнее, что происходит при запуске аутентифицированной сессии. Клиент передаёт в запросе логин и пароль пользователя. Если условный сервис аутентификации _authService (реализацию оставляю за вами) подтверждает правильность введенных данных, то мы создаем JWT-токен и сохраняем его в cookie со следующими параметрами:
1) HttpOnly = true
Ключевая настройка. Разрешает доступ к cookie только со стороны сервера, JS код не будет иметь к ней доступ.
2) SameSite =
SameSiteMode.Strict
Запрещает браузерам отправлять этот cookie при межсайтовых запросах. При необходимости можно снизить до SameSiteMode.Lax, но это повысит уязвимость к XSRF атакам (о них позже).
3) Secure = true
Cookie будет отправляться только при соединении HTTPS.
4) Expires = DateTimeOffset.Now.AddMinutes(options.ExpirationTime)
Время жизни cookie задаём таким же, как время жизни JWT-токена, потому что нет смысла хранить токен дольше.
При логауте или неправильных данных входа мы просто удаляем токен из cookies – по сути, отзываем токен.
Проверка токена через сервис JwtService
Код JwtService:
В JwtService мы создаем набор клеймов, которые хотим передавать с каждым запросом, и зашиваем их в токен. В моём примере я решил сохранить в токен информацию о логине, сессии и ролях пользователя.
А что за antiForgeryToken?
А вот и отрицательная сторона использования cookie: уязвимости к XSRF-атакам. Если кратко, то это вид атак, при которых злоумышленник заставляет браузер жертвы, уже авторизованной на сайте, отправить нежелательный запрос на этот сайт от её имени. Браузер автоматически добавляет авторизационный cookie к запросу, что позволяет выполнить действия от имени жертвы.
Частично защиту от XSRF атак повышает опция SameSite = SameSiteMode.Strict, но это не панацея. Также рекомендуют настраивать CORS-политики, но я оставлю эту тему за пределами этой статьи (главное, не забыть AllowCredentials() на сервере и WithCredentials() на клиенте).
Ещё один способ
защиты от XSRF атак – добавить ко всем запросам antiForgeryToken. В ASP.NET
Core MVC можно использовать services.AddAntiforgery(), тогда к каждой
форме на странице будет добавлено скрытое поле, которое заполняется сервером
при рендеринге страницы и отправляется при запросах на сервер. Но мне кажется,
что при использовании связки SPA + ASP.NET Core Backend готовый метод защиты не
применим, т.к. рендеринг страницы происходит на клиенте.
В своём проекте я решил, что буду генерировать токен самостоятельно и возвращать его при успешной аутентификации. Т.е. сам antiforgery токен может быть получен только тем, кто знает логин и пароль. После этого токен сохраняется на клиенте и подставляется в header при каждом запросе.
Ремарка 1.
Antiforgery-токен в этом случае уязвим к XSS, т.к. доступен из JS кода, но он сам по себе не дает доступа к сайту, а только подтверждает источник запросов. Тем не менее некая уязвимость остается, хоть и требует одновременной реализации XSS и XSRF атаки.
Ремарка 2.
Не каждому проекту подойдёт такая реализация. В моём antiforgery-токен живет сутки, и это нормально, т.к. пользователь заходит в систему каждый день. Для B2C сайтов с планируемой авторизацией раз 2-3 месяца такой подход может нести больше рисков. Решение за вами.
Проверка antiForgeryToken
Для проверки этого токена используется middleware:
Сначала определяется наличие AuthorizeAttribute, чтобы не проверять токен для анонимных эндпоинтов. Затем вызывается CheckToken у самописного IAntiForgeryService. Логику проверки можно реализовать любую. Например, сгенерировать новый токен по известным параметрам и сравнить с полученным от клиента. Если токены не соответствуют — возвращаем код ошибки. Я выбрал 400, т.к. antiForgeryToken — это, по-моему, не про авторизацию, поэтому не хотелось использовать 401 и 403.
Абсолютная власть
Главное преимущество cookie — в том, что возможность управления JWT-токеном на стороне сервера ограничена только вашей фантазией. Для себя я реализовал автоматический рефреш JWT-токена при каждом запросе без необходимости отдельного рефреш эндпоинта. Если для примера JWT-токен истекает через 15 минут, то при каждом запросе от авторизованного пользователя я продлеваю токен ещё на 15 минут.
Логику обновления можно менять под себя: например, ограничивать количество допустимых обновлений в рамках сессии и т.д.
А также можно написать другие middleware, которые в соответствии с бизнес-логикой будут отбирать или выдавать роли или даже отзывать токен «на лету».
Заключение
Использование HTTP-Only cookie для передачи JWT-токенов — это мощный паттерн, который эффективно защищает от XSS-атак и предоставляет серверу беспрецедентный контроль над сессией пользователя. Он позволяет реализовать такие функции, как немедленный отзыв токенов и их прозрачное обновление, что значительно повышает безопасность приложения. Однако этот подход требует тщательной реализации дополнительных мер защиты от XSRF-атак и продуманной политики управления жизненным циклом сессии. При грамотной настройке он становится надёжным фундаментом для системы аутентификации в современных веб-приложениях.
325 открытий2К показов



