Интервью-вопрос
Как правильно хранить токен во Frontend
Безопасное хранение токена зависит от угроз. Обычно не стоит класть чувствительный bearer-токен в localStorage, а для refresh token чаще выбирают HttpOnly, Secure cookie с CSRF-защитой.
- Добавлен
- Редакция
Подготовьте короткий ответ и пару деталей на случай уточняющих вопросов.
Мини-квиз
Проверка перед разбором
Несколько быстрых вопросов перед разбором. Так проще поймать места, которые только кажутся понятными.
Вопрос 1 из 50 правильно
Разбор
Разобраться, а не зазубрить
Дальше разбираем суть, типичные уточнения и места, где легко сказать лишнее или перепутать термины.
Базовая идея
Хороший ответ начинается не с названия хранилища, а с риска. Токен авторизации обычно работает как ключ к API. Если его украли, злоумышленник может делать запросы от имени пользователя, пока токен не истечет или не будет отозван.
Во frontend чаще сравнивают три варианта: Web Storage, память приложения и cookie. У каждого варианта своя цена. localStorage переживает перезагрузку страницы, но доступен JavaScript. Память приложения меньше подходит для кражи через простое чтение storage, но теряется при reload. HttpOnly cookie скрывает токен от JavaScript, но автоматически отправляется браузером и требует CSRF-защиты.
На интервью лучше не говорить, что токен всегда нужно хранить только в одном месте. Сильнее звучит так: вы выбираете вариант под угрозы и архитектуру. Это показывает, что вы понимаете компромисс, а не просто запомнили один совет.
Как выбрать подход
Для типичного веб-приложения безопасная отправная точка такая: refresh token хранить в HttpOnly, Secure cookie, access token делать короткоживущим. Access token можно держать в памяти, если API требует заголовок Authorization. Если приложение использует cookie-сессию, access token как отдельная строка на клиенте может вообще не понадобиться.
Важно не обещать абсолютной защиты. HttpOnly cookie не дает прочитать токен, но если на странице есть XSS, вредный скрипт может нажимать кнопки, отправлять формы и вызывать API в рамках текущей сессии. Поэтому хранение токена снижает ущерб, но не заменяет защиту от XSS.
Как выбрать место хранения
Не храните чувствительный токен в localStorage. Выбирайте HttpOnly cookie или access token в памяти.Используйте Secure, HttpOnly, SameSite cookie и добавьте CSRF-защиту для небезопасных методов.Держите access token короткоживущим в памяти и обновляйте его через refresh endpoint.Храните секреты на серверной стороне или в HttpOnly cookie. Не отдавайте токен в HTML.Что ломается в разных вариантах
Риски, которые стоит назвать
XSSУдобно переживает перезагрузку, но вредный скрипт может прочитать токен. Используйте только для данных, которые не дают доступ к аккаунту.
ReloadТокен не лежит в долговременном storage, но пропадает при обновлении вкладки. Нужен понятный refresh flow, повторный вход и очистка приватного UI при logout или ошибке refresh.
CSRFJavaScript не читает cookie, но браузер отправляет ее автоматически. Нужны SameSite, CSRF-токен и серверные проверки.
Hydration leakТокен нельзя класть в начальное состояние страницы. Сервер должен использовать cookie сам и отдавать клиенту только безопасные данные.
Long lifeДолгоживущий токен опаснее access token. Храните его строже, ограничивайте срок, path и умейте отзывать сессии.
Если вам нужно привести пример, покажите плохой вариант и сразу назовите последствие. Плохой пример выглядит так:
// Плохо: токен доступен любому JavaScript на странице.
localStorage.setItem("access_token", token);
const token = localStorage.getItem("access_token");
fetch("/api/profile", {
headers: { Authorization: `Bearer ${token}` },
});Проблема не в самом fetch. Если появится XSS, вредный скрипт прочитает ключ access_token, отправит токен наружу и злоумышленник сможет дергать API уже со своего устройства. Еще один практический баг: frontend может продолжать считать пользователя авторизованным, пока токен лежит в storage, даже если сервер уже отозвал сессию.
Безопаснее сделать access token короткоживущим и держать его в памяти, а refresh token выдавать сервером через HttpOnly cookie. В обертке над запросами не теряйте существующие заголовки и отдельно обрабатывайте 401.
// Идея безопаснее: access token живет в памяти и не переживает закрытие вкладки.
let accessToken: string | null = null;
export function setAccessToken(token: string) {
accessToken = token;
}
export function clearAccessToken() {
accessToken = null;
}
export async function apiFetch(input: RequestInfo, init: RequestInit = {}) {
const { headers: initHeaders, ...rest } = init;
const headers = new Headers(initHeaders);
if (accessToken) {
headers.set("Authorization", `Bearer ${accessToken}`);
}
const response = await fetch(input, {
...rest,
headers,
credentials: "include", // если refresh cookie нужна этому запросу
});
if (response.status === 401) {
clearAccessToken();
// Здесь же сбросьте auth state в UI и запустите refresh или logout flow.
}
return response;
}Это не делает приложение неуязвимым. При XSS вредный код все равно может вызывать API в открытой сессии. Но при обычной перезагрузке страницы токен исчезает из памяти, а долгоживущий refresh token не доступен через JavaScript, если сервер выдал его как HttpOnly cookie.
Cookie-флаги и CSRF
Если вы говорите про cookie, назовите флаги и зачем они нужны. HttpOnly запрещает доступ через document.cookie. Secure заставляет отправлять cookie только по HTTPS. SameSite ограничивает отправку cookie в cross-site сценариях.
Пример серверной установки cookie:
res.cookie("refresh_token", refreshToken, {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/auth/refresh",
maxAge: 1000 * 60 * 60 * 24 * 7,
});path не является полноценной границей безопасности, но помогает не отправлять refresh token на каждый endpoint. Для запросов, которые меняют состояние, все равно нужны серверные проверки. Обычно это CSRF-токен в заголовке плюс проверка Origin или Referer.
Refresh flow без гонок
Автоматическое обновление токена стоит описывать аккуратно. Наивный interceptor часто создает гонки. Пять запросов получают 401, каждый запускает refresh, часть запросов повторяется со старым токеном, сеть забивается лишними запросами, а UI показывает пользователя авторизованным после провала обновления.
Хорошая формулировка для интервью:
Я бы сделал один refresh-запрос на группу параллельных 401, ограничил повтор исходного запроса одним разом и при ошибке refresh очистил состояние авторизации. Так приложение не уходит в бесконечный цикл и не показывает пользователю приватный UI без валидной сессии.
- 1Access token живет недолго и хранится в памяти
- 2Refresh token лежит в HttpOnly, Secure cookie
- 3При 401 выполняется один refresh-запрос, остальные запросы ждут
- 4После неуспешного refresh токен и приватный UI очищаются
- 1Оба токена лежат в localStorage
- 2Каждый запрос при 401 сам запускает refresh
- 3Старый запрос бесконечно повторяется без лимита
- 4После ошибки UI считает пользователя авторизованным и показывает приватные данные
Что сказать про CSP, CORS и дополнительные меры
CSP полезна как дополнительный слой против XSS. Она может запретить inline-скрипты и ограничить источники скриптов. Но CSP не заменяет безопасный рендеринг данных и не исправляет уже выданный токен, который доступен JavaScript.
CORS нужен, чтобы браузер контролировал чтение ответов между origin. Но CORS не является способом хранения токена и не должен быть главным ответом на вопрос про токены. Не рассчитывайте, что CORS исправит XSS внутри вашего origin или заменит CSRF-защиту для cookie. Сервер обязан проверять авторизацию на каждом запросе, а не полагаться на то, что frontend не покажет кнопку.
Сильный ответ можно закончить так: я уменьшаю вероятность кражи токена, ограничиваю срок его жизни, защищаю refresh flow, закрываю CSRF для cookie и не отдаю секреты в клиентское состояние.
Практический вывод
Короткая версия ответа: не храните чувствительные токены в localStorage по умолчанию. Для браузерного приложения чаще выбирайте HttpOnly, Secure cookie для refresh token или cookie-сессию, добавляйте SameSite и CSRF-защиту, а access token делайте короткоживущим.
Если архитектура требует bearer-токен в заголовке, держите его в памяти и продумайте обновление. Если приложение SSR, не сериализуйте токен в HTML и не кладите его в hydrated state. На интервью обязательно называйте и XSS, и CSRF. Так вы показываете, что понимаете компромисс, а не просто запомнили слово HttpOnly.
Частые ошибки
Где обычно ошибаются
Проверьте формулировки, которые звучат уверенно, но на интервью быстро выдают пробелы.
- 1
Называть localStorage нормой
localStorage часто выбирают из-за простоты, но он доступен JavaScript. При XSS токен можно украсть и использовать вне браузера пользователя. Лучше сказать, что это компромисс для низкорисковых сценариев, а для авторизации нужны более строгие варианты.
- 2
Считать память приложения полной защитой
Access token в памяти лучше, чем тот же токен в долговременном storage, но это не абсолютная защита. При XSS вредный код все еще может дергать API в текущей сессии. На практике нужно очищать токен и пользовательское состояние при logout, не сериализовать токен в storage и правильно обрабатывать
401. - 3
Забывать про CSRF у cookie
HttpOnlyзащищает от чтения cookie, но не от автоматической отправки cookie браузером. Если API меняет данные, добавьтеSameSite, CSRF-токен и проверкуOriginна сервере. Иначе чужая страница может попытаться выполнить действие от имени пользователя. - 4
Верить, что CORS защищает токен
CORS ограничивает чтение ответа из другого origin, но не исправляет XSS внутри вашего сайта и не заменяет авторизацию на сервере. Не говорите, что CORS сам защищает токены. Его стоит упоминать как часть политики доступа, а не как главный механизм хранения.
- 5
Отдавать токен в hydrated state
В SSR легко случайно положить токен в JSON, который встраивается в HTML. После этого токен снова доступен через JavaScript и расширения браузера. Правильнее использовать токен на сервере и не сериализовать его в клиентское состояние.
- 6
Делать бесконечный refresh loop
Если interceptor повторяет запрос после каждого
401без флага повтора и лимита, приложение может зациклить сеть. Безопаснее помечать уже повторенный запрос, объединять параллельные refresh-запросы и завершать сессию при ошибке обновления.
Follow-up
Что могут спросить дальше
Короткие ответы на вопросы, которыми проверяют понимание хранения токенов, XSS, CSRF и SSR.
Живые ответы
Видео с похожим вопросом
Если найдем публичные интервью с таким вопросом, добавим их сюда. Их удобно смотреть после теории, чтобы свериться с живыми ответами.
Пока видео нет. Когда появятся подходящие публичные интервью, добавим их в этот блок, чтобы можно было сравнить разбор с тем, как отвечают реальные кандидаты.
Что такое OAuth 2.0 😎
OAuth 2.0 это протокол авторизации для делегированного доступа к ресурсам без передачи пароля. Разбираем роли, токены, Authorization Code Flow с PKCE и типичные риски для frontend-приложений.
С какими системами авторизации работал 😎
На такой вопрос лучше отвечать через реальный опыт, схему входа, хранение токенов и обработку ошибок. Разбираем, как не звучать общо и показать понимание рисков безопасности на фронтенде.