Почему важно правильно хранить JWT?
Раз уж мы начали говорить об аутентификации, возникает вопрос: где хранить наш драгоценный токен? Вариантов много, но мы рассмотрим два самых популярных — localStorage и sessionStorage. Однако, важно понимать, что выбор места для хранения токена напрямую влияет на безопасность вашего приложения.
Чего стоит бояться
- XSS (межсайтовый скриптинг): если злоумышленник внедрит вредоносный скрипт в ваш код, токен в
localStorageможет стать доступным для кражи. - CSRF (межсайтовая подделка запроса): при неправильной настройке серверов некоторые схематические уязвимости можно использовать против вас, но чаще это затрагивает cookie.
Итак, давайте посмотрим, как localStorage и sessionStorage работают, и разберём их плюсы и минусы.
LocalStorage vs SessionStorage: Чем они отличаются?
| Характеристика | localStorage | sessionStorage |
|---|---|---|
| Срок хранения | Данные хранятся «навсегда» (или до явного удаления). | Данные удаляются при закрытии вкладки браузера. |
| Глобальная доступность | Доступно между вкладками одного и того же домена. | Доступно только в рамках одной вкладки. |
| Объём хранилища | Обычно до 5 МБ. | Обычно до 5 МБ. |
Зная это, вы можете выбрать место хранения в зависимости от сценария. Например, если сессии пользователя должны быть недолгими, sessionStorage подойдёт идеально. Если же требуется кэшировать авторизацию на более долгий срок, можно воспользоваться localStorage.
Хранение токенов в localStorage
Итак, давайте начнём с простого примера, как хранить наши токены в localStorage.
Для этого мы можем использовать встроенные методы localStorage.setItem.
// Сохраняем токен в localStorage
const saveToken = (token: string): void => {
localStorage.setItem('authToken', token);
};
Извлечение токена
Для получения значения используем localStorage.getItem.
// Получаем токен из localStorage
const getToken = (): string | null => {
return localStorage.getItem('authToken');
};
Удаление токена
Если пользователь разлогинился, токен нужно удалить.
// Удаляем токен из localStorage
const removeToken = (): void => {
localStorage.removeItem('authToken');
};
Пример использования в сервисе
Ваш AuthService может выглядеть примерно так:
class AuthService {
private static TOKEN_KEY = 'authToken';
static saveToken(token: string): void {
localStorage.setItem(AuthService.TOKEN_KEY, token);
}
static getToken(): string | null {
return localStorage.getItem(AuthService.TOKEN_KEY);
}
static removeToken(): void {
localStorage.removeItem(AuthService.TOKEN_KEY);
}
}
Теперь AuthService предоставляет удобные методы для работы с токенами.
Хранение токенов в sessionStorage
Работа с sessionStorage аналогична. Просто заменяем localStorage на sessionStorage. Например:
Сохранение токена
// Сохраняем токен в sessionStorage
const saveToken = (token: string): void => {
sessionStorage.setItem('authToken', token);
};
Извлечение токена
// Получаем токен из sessionStorage
const getToken = (): string | null => {
return sessionStorage.getItem('authToken');
};
Удаление токена
// Удаляем токен из sessionStorage
const removeToken = (): void => {
sessionStorage.removeItem('authToken');
};
Пример для AuthService
Вы можете расширить наш AuthService так, чтобы он поддерживал оба хранилища:
class AuthService {
private static TOKEN_KEY = 'authToken';
static saveToken(token: string, useSession: boolean = false): void {
if (useSession) {
sessionStorage.setItem(AuthService.TOKEN_KEY, token);
} else {
localStorage.setItem(AuthService.TOKEN_KEY, token);
}
}
static getToken(useSession: boolean = false): string | null {
return useSession
? sessionStorage.getItem(AuthService.TOKEN_KEY)
: localStorage.getItem(AuthService.TOKEN_KEY);
}
static removeToken(useSession: boolean = false): void {
if (useSession) {
sessionStorage.removeItem(AuthService.TOKEN_KEY);
} else {
localStorage.removeItem(AuthService.TOKEN_KEY);
}
}
}
Теперь вы можете выбирать, в какое хранилище сохранять токен!
Безопасность при хранении токенов
А теперь о важном: токены в localStorage или sessionStorage не зашифрованы. Это означает, что они видны через инструменты разработчика. Да, вы можете написать логику скрытия, но это не панацея.
Рассмотрим несколько советов по безопасности:
Выбор хранилища
Если данные крайне критичны (например, доступ к банкам), подумайте о хранении токенов в HttpOnly cookies, а не в localStorage. Такие куки недоступны из JavaScript, что значительно снижает риск XSS-атак.
Использование HTTPS
Ваш сайт должен работать только по https. Без этого любые данные, включая токены, могут перехватить.
Ограничение времени действия токена
Всегда используйте короткоживущие токены (например, 15-30 минут), чтобы даже в случае их кражи ущерб был минимальным.
FAQ: что выбрать — localStorage или sessionStorage?
Когда использовать localStorage?
- Когда вы хотите, чтобы пользователь оставался залогиненным даже после закрытия браузера.
Когда использовать sessionStorage?
- Когда вы хотите, чтобы пользователь автоматически разлогинивался при закрытии вкладки.
А если безопасность — приоритет?
- Тогда используйте
HttpOnly cookies. Это наиболее безопасный способ хранения токенов.
- Тогда используйте
Как это выглядит в приложении?
В нашем проекте есть страница логина. После успешного входа мы сохраняем токен в localStorage и используем его для последующих запросов.
const login = async (username: string, password: string) => {
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
if (!response.ok) {
throw new Error('Failed to login');
}
const { token } = await response.json();
AuthService.saveToken(token);
console.log('Logged in!');
} catch (error) {
console.error('Error during login:', error);
}
};
Теперь вы знаете, как хранить JWT в клиенте. Как видите, всё не так уж сложно, но требует понимания нюансов и мер предосторожности.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ