Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Хранение токенов аутентификации в браузере
Выбор места для хранения токена — это критический вопрос безопасности в веб-приложениях. Различные хранилища имеют разные уровни защиты от различных типов атак.
Основные варианты хранения
1. localStorage — В оперативной памяти браузера
// Сохранение токена
localStorage.setItem('authToken', token);
// Получение токена
const token = localStorage.getItem('authToken');
// Удаление токена
localStorage.removeItem('authToken');
Характеристики:
- Постоянное хранилище (пережит закрытие вкладки)
- Объем: 5-10 МБ
- Доступно для JavaScript кода
- Уязвимо для XSS атак
- Синхронное хранилище
2. sessionStorage — Временное хранилище
// Сохранение токена
sessionStorage.setItem('authToken', token);
// Получение токена
const token = sessionStorage.getItem('authToken');
Характеристики:
- Удаляется при закрытии вкладки
- Разные вкладки имеют разные хранилища
- Уязвимо для XSS атак
- Хорошо для одноразовых сессий
3. HttpOnly Cookie — Самый безопасный вариант
// На сервере при отправке ответа:
// Set-Cookie: authToken=xyz; HttpOnly; Secure; SameSite=Strict
// На клиенте: недоступно для JavaScript!
// console.log(document.cookie); // пусто
Характеристики:
- Недоступно для JavaScript (защита от XSS)
- Автоматически отправляется с каждым запросом
- Может быть Secure (только HTTPS) и SameSite (защита от CSRF)
- Требует конфигурации на сервере
- Защищено от атак
4. In-Memory (переменная в памяти)
let authToken = null;
export function setToken(token) {
authToken = token;
}
export function getToken() {
return authToken;
}
// Потеряется при перезагрузке страницы!
Характеристики:
- Хранится только в памяти JS приложения
- Удаляется при перезагрузке страницы
- Не доступно для других вкладок
- Максимальная безопасность
Сравнение способов
| Способ | Постоянность | XSS уязвимо | CSRF уязвимо | Производительность |
|---|---|---|---|---|
| localStorage | Да | Да | Нет | Синхронно (медленно) |
| sessionStorage | До закрытия | Да | Нет | Синхронно (медленно) |
| HttpOnly Cookie | Да | Нет | Нет (с SameSite) | Автоматически |
| In-Memory | Нет | Нет | Нет | Очень быстро |
Рекомендуемый подход
Лучшая практика: комбинированное хранение
// Access Token: In-Memory (краткосрочный, чувствительный)
let accessToken = null;
// Refresh Token: HttpOnly Cookie (долгосрочный, защищенный)
// Устанавливается сервером, недоступен JavaScript
function setAccessToken(token) {
accessToken = token;
}
function getAccessToken() {
return accessToken;
}
// При перезагрузке страницы
// Access Token = null
// Refresh Token = в cookie (сервер может выдать новый access token)
Практическая реализация с HttpOnly Cookie
На сервере (FastAPI):
from fastapi import Response
from fastapi.responses import JSONResponse
@app.post('/api/v1/auth/login')
def login(credentials):
user = authenticate_user(credentials)
access_token = create_access_token(user)
refresh_token = create_refresh_token(user)
response = JSONResponse(
content={"access_token": access_token}
)
response.set_cookie(
key="refresh_token",
value=refresh_token,
httponly=True, # Защита от XSS
secure=True, # Только HTTPS
samesite="strict", # Защита от CSRF
max_age=7*24*60*60 # 7 дней
)
return response
На клиенте (React):
// login.ts
export async function login(email, password) {
const response = await fetch('/api/v1/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include', // Важно! Отправить cookie
body: JSON.stringify({ email, password })
});
const data = await response.json();
// Сохранить access token в памяти
setAccessToken(data.access_token);
}
// authContext.ts
let accessToken = null;
export function setAccessToken(token) {
accessToken = token;
}
export function getAccessToken() {
return accessToken;
}
// api.ts
export async function apiCall(endpoint, options = {}) {
const token = getAccessToken();
const headers = {
...options.headers,
'Authorization': `Bearer ${token}`
};
return fetch(endpoint, {
...options,
headers,
credentials: 'include' // Отправить refresh token в cookie
});
}
Обновление токена при истечении
Автоматическое обновление:
export async function ensureValidToken() {
if (!isTokenExpired(accessToken)) {
return accessToken;
}
try {
const response = await fetch('/api/v1/auth/refresh', {
method: 'POST',
credentials: 'include' // Отправить refresh token
});
if (response.ok) {
const data = await response.json();
setAccessToken(data.access_token);
return data.access_token;
}
} catch (error) {
// Refresh не удался, нужно переавторизоваться
setAccessToken(null);
navigateTo('/login');
}
}
Защита от основных атак
Защита от XSS:
- Используй HttpOnly Cookie для refresh token
- Access token в памяти, не в localStorage
- Санитизируй пользовательский ввод
- Используй CSP (Content Security Policy)
Защита от CSRF:
- Используй SameSite на cookie
- CSRF токены для изменяющих операций
- Проверяй Origin заголовок
Защита от перехвата:
- Всегда используй HTTPS
- Установи Secure флаг на cookie
- Короткое время жизни access token
Что НЕ нужно делать
// Плохо: Хранить token в localStorage
localStorage.setItem('token', token); // Уязвимо для XSS!
// Плохо: Хранить в URL
window.location.href = '/dashboard?token=xyz'; // Видно в истории!
// Плохо: Отправлять в открытом виде
fetch('/api/data?token=xyz'); // Видно в логах!
// Плохо: Одинаковый токен навечно
// Используй refresh токены
Лучшие практики
- Access Token: In-Memory или очень короткий HttpOnly Cookie
- Refresh Token: HttpOnly Cookie с защитой (Secure, SameSite)
- Разные время жизни: Access 15 минут, Refresh 7 дней
- HTTPS только: Никогда не отправляй токены по HTTP
- Санитизация: Защищайся от XSS атак
- Валидация: Проверяй токены на сервере
- Обновление: Обновляй access token автоматически