Webrium
Подписаться

Токен в браузере: где его хранить

Вопрос о месте хранения решается моделью угрозы, а не удобством. Две популярные ответа защищают от разных вещей, и одно хранилище не даёт обоих свойств.

Две угрозы по разные стороны: одна закрывается хранилищем, другая — серверомчужой скриптчужой сайт

Вопрос «где хранить токен в браузере» задают как вопрос о месте, а ответ на него определяется не местом, а тем, от какой угрозы вы защищаетесь. Два популярных варианта защищают от разных вещей, и спор о том, какой лучше, без указания угрозы не имеет решения.

Две угрозы, а не одна

Чужой скрипт на вашей странице. Код, попавший на страницу через уязвимость или через зависимость, выполняется с теми же правами, что и ваш. Он может прочитать всё, к чему у страницы есть доступ.

Чужой сайт, действующий от имени пользователя. Страница на другом домене отправляет запрос к вашему серверу, и браузер прикладывает к нему сохранённые данные автоматически. Пользователь ничего не нажимал осознанно.

Эти угрозы защищаются противоположными свойствами. От первой спасает недоступность данных для скрипта. От второй — контроль над тем, когда данные прикладываются к запросу. Одно хранилище не даёт обоих свойств бесплатно.

Что даёт каждое место

Хранилище страницы. Данные доступны скрипту и не отправляются никуда сами. Значит, защищены от второй угрозы полностью и от первой — никак: любой выполнившийся на странице код прочитает токен и отправит куда угодно.

Cookie, недоступная скрипту. Помечена так, что код страницы её не видит. Защищена от первой угрозы: чужой скрипт не может её прочитать. Но браузер прикладывает её к запросам автоматически, включая запросы, инициированные другим сайтом, — значит, вторая угроза остаётся и закрывается отдельно.

Отсюда вывод, который обычно и является ответом: cookie, недоступная скрипту, плюс явная защита от запросов со стороны. Это единственная комбинация, закрывающая обе угрозы, и она требует работы на сервере, а не только выбора места.

Защита от запросов со стороны сегодня складывается из двух вещей. Первая — признак, ограничивающий отправку cookie при переходах с чужих сайтов; он закрывает большую часть случаев и настраивается одним значением. Вторая — проверка происхождения запроса на сервере: заголовок, сообщающий, откуда пришёл запрос, сверяется со списком разрешённых. Ни одна из двух не заменяет другую полностью, поэтому ставят обе.

Тупик: спрятать токен получше

Первое, что пробуют, — усложнить доступ: зашифровать значение перед сохранением, разложить по частям, положить в переменную, а не в хранилище.

Это не помогает, потому что не меняет модель угрозы. Скрипт, выполняющийся на странице, имеет доступ ко всему, к чему имеет доступ страница, включая ключ шифрования и код, который собирает токен из частей. Он может просто дождаться момента, когда токен соберут, и прочитать готовое.

Разновидность того же — хранить токен в памяти и не сохранять никуда. Это действительно снижает риск: токен исчезает при перезагрузке страницы, и окно возможностей у чужого кода короче. Но пока страница открыта, он доступен так же, как и раньше, — а именно тогда чужой скрипт и работает.

Общая ошибка: попытка защититься от кода, выполняющегося в том же контексте, средствами того же контекста.

Что действительно меняет картину

Короткий срок жизни и обновление. Токен, живущий минуты, ограничивает ущерб от кражи: украденное быстро протухает. Обновление делается отдельным долгоживущим значением, которое хранится строже и используется реже.

Привязка к признакам сеанса. Токен, действительный только вместе с определённым признаком — устройством, отпечатком соединения, — становится бесполезен, будучи украденным и использованным в другом месте. Полностью проблему не решает, но повышает цену атаки.

Ограничение области действия. Токен, дающий доступ ко всему, и токен, дающий доступ к одной операции, стоят по-разному при краже. Разделение прав — это работа не в браузере, а в проектировании интерфейса сервера.

Возможность отозвать. Токен, который нельзя отозвать до истечения срока, превращает любой инцидент в ожидание. Список отозванных или короткий срок с обновлением — выбор между двумя видами расходов, но что-то из этого должно быть.

Здесь стоит назвать неочевидную цену самодостаточных токенов, которые проверяются без обращения к хранилищу. Их удобство — в том, что сервер не ходит в базу при каждом запросе. Их цена — в том, что отозвать такой токен нечем: он действителен, пока не истёк, и смена пароля пользователем его не отменяет. Либо вы заводите список отозванных и теряете исходное преимущество, либо соглашаетесь, что окно после инцидента равно сроку жизни токена. Это решение принимают один раз и осознанно, а не обнаруживают в день происшествия.

Чего не отменяет выбор хранилища

Ни одно место хранения не спасает, если чужой скрипт вообще попал на страницу. Поэтому работа начинается не с выбора хранилища, а с того, чтобы чужого кода на странице не было: экранирование данных при выводе, политика источников содержимого, контроль над зависимостями.

Хранилище — это то, что уменьшает ущерб, когда предыдущие меры не сработали. Начинать с него — значит готовиться к последствиям, не занимаясь причиной.

Проверить это соотношение просто. Спросите себя, что произойдёт, если на страницу попадёт чужой скрипт. Если ответ «он украдёт токен» — вы выбираете хранилище. Если ответ «он сделает от имени пользователя всё что угодно, даже не читая токен» — хранилище здесь ни при чём, и работать надо выше.

Отдельно: токен в адресной строке недопустим при любом хранилище. Адрес попадает в журналы сервера, в историю браузера и в заголовок перехода на чужой сайт.

И ещё одно, о чём вспоминают поздно: выход из системы должен что-то делать на сервере, а не только очищать хранилище в браузере. Удаление токена на стороне клиента не отменяет его действительность — украденная до выхода копия продолжит работать. Если отзыв невозможен по устройству токена, короткий срок жизни остаётся единственным, что ограничивает последствия.

Позиция редакции

Мы считаем, что для веб-приложения ответ по умолчанию — cookie, недоступная скрипту, с явной защитой от запросов со стороны и коротким сроком жизни. Практический вывод: если у вас токен лежит в хранилище страницы и это выбрано «потому что проще работать с запросами», вы приняли решение по удобству, а не по угрозе, и стоит это хотя бы записать осознанно.

Мы неправы, если у приложения нет собственного домена и cookie использовать нельзя — например, это виджет, встраиваемый на чужие сайты. Там выбор ограничен, и правильный ответ строится вокруг короткого срока жизни и ограниченных прав, а не вокруг места хранения.

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

Цепочка шагов конвейера CI: чужой код входит, исполняется с ключами, и последний шаг — публикация — штатно выводит секрет за границу доверияграница довериясекрет наружу
Предыстория

Секреты в CI: пять типовых утечек

База: почему хранилище секретов и маскирование логов не спасают, если не знать, каким именно образом секрет утекает.

На шкале времени жизни экрана величина — момент, когда становятся известны входные данные, — пробивает пороговую линию между сервером и клиентом: если данные изсервер/клиентвремя жизни экрана
Практика

Где проходит граница клиент — сервер

Куда именно провести границу в своём приложении: не по типам компонентов, а по моменту, когда становятся известны входные данные.

Два маршрута обновления зависимостей: короткий путь откладывания обходится дорого, длинный путь регулярных обновнений — дёшевоОтложить — дорогоОбновлять — дёшево
Усиление

Политика обновления зависимостей, которую соблюдают

Подтверждает с процессной стороны: политика обновлений держится не матрицей дедлайнов, а разделением рутины и безопасности.

Три показателя безопасности паролей: два в норме, один за шкалой — отсутствие солиБез соли
Практика

Пароли пользователей: как хранить и что проверить сегодня

Как за час проверить и исправить уязвимости в хранении паролей в своём проекте — от алгоритмов хеширования до проверки соли.

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

Начните вводить — материалы появятся здесь.

выбрать · Enter открыть · Esc закрыть