← Назад к вопросам

Где желательно хранить Token?

2.0 Middle🔥 131 комментариев
#JavaScript Core

Комментарии (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 токены

Лучшие практики

  1. Access Token: In-Memory или очень короткий HttpOnly Cookie
  2. Refresh Token: HttpOnly Cookie с защитой (Secure, SameSite)
  3. Разные время жизни: Access 15 минут, Refresh 7 дней
  4. HTTPS только: Никогда не отправляй токены по HTTP
  5. Санитизация: Защищайся от XSS атак
  6. Валидация: Проверяй токены на сервере
  7. Обновление: Обновляй access token автоматически