[Из песочницы] О хранении JWT токенов в браузерах
Открытый стандарт JWT официально появился в 2015 (rfc7519) обещая интересные особенности и широкие перспективы. Правильное хранение Access токена является жизненно важным вопросов при построении системы авторизации и аутентификации в современном Web, где становятся все популярнее сайты, построенные по технологии SPA.
Неправильное хранение токенов ведет к их краже и переиспользованию злоумышленниками.
Так и где хранить?
Рассмотрим основные варианты хранения JWT Access токена в браузере:
- Local Storage/Session Storage – метод не безопасный и подвержен атакам типа XSS, особенно если Вы подключаете скрипты из сторонних CDN (добавление integrity атрибута не может гарантировать 100% безопасность), либо не уверены что подключаемые Вами скрипты не имеют возможности «слить» данные из хранилищ на сторону. Более того если Local Storage доступен между табами то Session Storage доступен только в одной вкладке и открытие сайта в новой вкладке лишь вызовет новый раунд авторизации/рефреша Access токена.
- Хранение токена в локальной переменной внутри замыкания тоже не обеспечивает должной безопасности потому что атакующий может, например, проксировать функцию fetch и отправить токен на левый сайт. Так же это не решает проблему двух вкладок – нет безопасного способа передать токен из одной вкладки в другую.
- Cookies. Вот мы вернулись к старым «печенькам» которые использовались для хранения cookie sessions. Простое хранения Access токена в cookie чревато атакой CSRF. Более того оно не защищает от XSS атак. Для защиты от CSRF нужно ставить параметр Cookie SameSite в режим Strict– этим можно добиться того что все запросы, которые идут с других сайтов, не будут содержать Ваши credentials, что автоматически лишит атакующего возможности произвести CSRF атаку.
В отличии от первых двух вариантов здесь есть и плюс – Access токен невозможно получить через JS если использовать флаг httpOnly, добавление Secure так же усилит защиту от сниффинга.Важным моментом является установка Cookie только для api домена/пути, чтобы запросы к публичной статике не содержали оверхед в header.
Что в итоге?
Cookies при правильном использовании являются адекватным и наиболее безопасным на данный момент решением для хранения JWT Access токена и должны следовать следующим правилам:
- Быть установленными для API домена/пути чтобы избежать оверхеда при запросах к статичным файлам (публичным картинкам/стилям/js файлам).
- Иметь флаг Secure (для передачи только по https).
- Иметь флаг httpOnly (для невозможности получения доступа из JavaScript).
- Атрибут SameSite должен быть Strict для защиты от CSRF аттак, запретит передачу Cookie файлов если переход к вашему API был не с установленого в Cookie домена.
На стороне сервера так же должно быть настроено:
- Content-Security-Policy – ограничение доверенных доменов для предотвращения возможных XSS атак
- Заголовок X-Frame-Options для защиты от атак типа clickjacking.
- X-XSS-Protection – принудительно включить встроенный механизм защиты браузера от XSS атак.
- X-Content-Type-Options – для защиты от подмены MIME типов.
Соблюдение этих мер в купе с частой ротацией Access/Refresh токенов должно помочь обеспечить высокий уровень безопасности на сайте.
Ограничения
Не смотря на то что атрибут SameSite поддерживается во многих популярных браузерах , существуют так же браузеры которые не поддерживают его или поддерживают частично (привет IE и Safari для мака). Для этих случаев нужен fallback к CSRF токенам. В этом случае вместе с запросами к API надо передавать и CSRF токен. Правильный CSRF токен должен генерироваться сервером с учетом Fingerprint’a пользователя да бы минимизировать вероятность его подмены.