Где хранится JWT токен?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Хранение JWT токена: подходы, безопасность и лучшие практики
Веб-приложения используют JSON Web Token (JWT) для аутентификации и передачи данных между сторонами. Место хранения токена на клиентской стороне — критически важный архитектурный выбор, влияющий на безопасность и удобство пользователя. Вот основные подходы, их плюсы, минусы и рекомендации.
Основные места хранения на клиенте
1. LocalStorage (или SessionStorage)
Наиболее распространённый, но спорный с точки зрения безопасности метод.
// Пример сохранения токена в localStorage после успешного логина
const login = async (credentials) => {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
});
const data = await response.json();
if (data.token) {
localStorage.setItem('jwt_token', data.token); // Сохранение
}
};
// Пример использования токена из localStorage для авторизованного запроса
const fetchData = async () => {
const token = localStorage.getItem('jwt_token');
const response = await fetch('/api/protected', {
headers: {
'Authorization': `Bearer ${token}`
}
});
return response.json();
};
- Преимущества: Простота реализации, токен доступен во всех вкладках/окнах одного источника (origin).
- Недостатки: Уязвим к XSS-атакам (Cross-Site Scripting). Зловредный JavaScript, внедрённый на страницу, может получить токен из хранилища.
2. HttpOnly Cookies
Более безопасный метод, рекомендованный OWASP для защиты от XSS.
// Бэкенд (Node.js/Express пример) устанавливает токен в HttpOnly куку
app.post('/login', (req, res) => {
// ... проверка логина и пароля
const token = generateJwt(user);
res.cookie('jwt_token', token, {
httpOnly: true, // Ключевая настройка! Cookie недоступна из JavaScript
secure: true, // Передача только по HTTPS (обязательно в продакшене)
sameSite: 'strict' // Защита от CSRF-атак
});
res.json({ message: 'Успешный вход' });
});
// Фронтенду не нужно вручную прикреплять токен к запросам.
// Браузер автоматически отправляет куку с каждым запросом на домен.
- Преимущества: Недоступность токена для JavaScript делает его неуязвимым к большинству XSS-атак.
- Недостатки: Требует настройки CORS (Cross-Origin Resource Sharing) и защиты от CSRF-атак (используя
sameSiteфлаги и/или CSRF-токены). Токен автоматически отправляется со всеми запросами к домену, что может упростить некоторые атаки, если кука не настроена корректно.
3. In-memory (или "Без сохранения")
Токен хранится только в оперативной памяти JavaScript (например, в замыкании или переменной состояния фреймворка типа React/Vue).
// Пример с использованием React Context/State
import React, { useState, createContext, useContext } from 'react';
const AuthContext = createContext(null);
export const AuthProvider = ({ children }) => {
const [token, setToken] = useState(null); // Токен живет только в состоянии компонента
const login = (newToken) => setToken(newToken);
const logout = () => setToken(null);
return (
<AuthContext.Provider value={{ token, login, logout }}>
{children}
</AuthContext.Provider>
);
};
// Использование токена для запроса
const useApi = () => {
const { token } = useContext(AuthContext);
const call = (url) => fetch(url, {
headers: { 'Authorization': `Bearer ${token}` }
});
return { call };
};
- Преимущества: Максимальная безопасность. Токен полностью исчезает при закрытии вкладки браузера, недоступен для XSS после перезагрузки страницы.
- Недостатки: Пользователь вынужден логиниться заново при каждой перезагрузке страницы или открытии новой вкладки. Не подходит для долгоживущих сессий.
4. Refresh Token + Access Token в Secure Storage
Продвинутый и самый безопасный паттерн, особенно для SPA (Single Page Applications).
- Короткоживущий Access Token (JWT) хранится в памяти приложения.
- Долгоживущий Refresh Token хранится в HttpOnly, Secure, SameSite куке (или в безопасном серверном хранилище сессий).
// Схематичный пример работы:
// 1. Логин: сервер возвращает Access Token (в теле ответа) и устанавливает Refresh Token в HttpOnly куку.
// 2. Клиент использует Access Token для API-запросов.
// 3. При истечении срока Access Token (получена 401 ошибка), клиент делает специальный запрос к `/refresh` endpoint.
// 4. Браузер автоматически отправляет Refresh Token (куку) на этот endpoint.
// 5. Сервер валидирует Refresh Token и выдает новый Access Token.
// Фронтенд: перехват 401 ошибки и попытка обновить токен
let refreshTokenRequest = null;
async function refreshAuthToken() {
if (!refreshTokenRequest) {
// Запрос на обновление. Refresh Token отправится автоматически в куках.
refreshTokenRequest = fetch('/api/auth/refresh', { method: 'POST' })
.then(res => res.json())
.then(data => {
refreshTokenRequest = null;
return data.accessToken; // Новый короткоживущий токен
});
}
return refreshTokenRequest;
}
Выводы и рекомендации
Выбор стратегии зависит от требований приложения к безопасности и UX:
- Для максимальной безопасности (банковские приложения, админ-панели): используйте связку Refresh Token в HttpOnly Cookie + короткоживущий Access Token в памяти. Это баланс безопасности (защита от XSS и CSRF) и удобства (пользователь остается в системе).
- Для стандартных веб-приложений: HttpOnly Cookie с правильно настроенными флагами (
Secure,SameSite=Strict/Lax) и защитой от CSRF — надежный и распространённый выбор. - LocalStorage/SessionStorage стоит использовать только для токенов, не содержащих критичных данных, в защищённых от XSS приложениях, либо для внутренних инструментов. Всегда помните о рисках.
- Чистая In-memory стратегия подходит для приложений с высочайшими требованиями к безопасности, где выход из системы при закрытии вкладки является приемлемым сценарием.
Итог: Не существует единственно правильного ответа. HttpOnly Cookie сегодня считается best practice для большинства сценариев, но архитектура Refresh + Access Token обеспечивает более тонкий контроль и повышенную безопасность, особенно в современных SPA и нативных мобильных приложениях.