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

Почему сохраняешь токен не в cookie?

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

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Почему я предпочитаю не хранить токены в cookies?

При работе с аутентификацией в современных веб-приложениях выбор места хранения токенов (обычно JWT) — критически важное архитектурное решение. Хотя cookie — традиционный и поддерживаемый браузерами механизм, в контексте SPA (Single Page Application) и RESTful API предпочтительнее хранить токены в localStorage или sessionStorage, а передавать через Authorization header. Вот ключевые причины, основанные на моем опыте разработки фронтенда.

1. Защита от CSRF-атак

Cookies автоматически отправляются браузером с каждым запросом к домену, включая поддельные запросы, инициированные злоумышленником (CSRF — Cross-Site Request Forgery). Это делает приложение уязвимым, если не используются дополнительные меры (например, CSRF-токены, SameSite атрибуты).

  • Токен в localStorage/sessionStorage не отправляется автоматически. Его необходимо явно извлечь и добавить в заголовок запроса (обычно Authorization: Bearer <token>). Это полностью исключает классические CSRF-атаки, так как злоумышленник не может заставить браузер отправить произвольный заголовок с другого сайта.

2. Больший контроль над логикой отправки

Cookies привязаны к домену и пути, их поведение (например, HttpOnly, Secure, SameSite) определяется сервером при установке. Фронтенд имеет ограниченный контроль.

  • Токен в хранилище браузера дает разработчику полный контроль:
    • Когда и куда отправлять токен (только к определенным эндпоинтам API).
    • Возможность легко реализовать механизм refresh токенов (отправлять старый access token для обновления, не дожидаясь истечения срока).
    • Упрощенная работа с несколькими доменами API (CORS), где cookies могут создавать проблемы.

3. Отсутствие ограничений по размеру

Cookies имеют жесткое ограничение ~4KB на домен, и все cookies отправляются с каждым запросом, увеличивая объем передаваемых данных.

  • localStorage/sessionStorage предоставляют гораздо больший объем (~5-10MB в зависимости от браузера), что важно для токенов с расширенными claims (полезными данными) или для хранения дополнительной информации сессии на клиенте.

4. Четкое разделение ответственности

В современной архитектуре backend (API сервер) и frontend (SPA) часто развернуты на разных доменах или даже независимо (серверы статики, CDN).

  • Cookies требуют дополнительной настройки CORS (withCredentials: true, специфичные заголовки ответа), усложняя конфигурацию.
  • Использование заголовка Authorization — более чистый и стандартный подход для REST/GraphQL API, не зависящий от происхождения фронтенда.

Пример кода: Типичная реализация с localStorage

// services/authService.js
const API_URL = 'https://api.example.com';

export const authService = {
    async login(credentials) {
        const response = await fetch(`${API_URL}/auth/login`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(credentials)
        });
        const data = await response.json();
        
        if (data.accessToken) {
            // Сохраняем токен в localStorage
            localStorage.setItem('accessToken', data.accessToken);
            // Опционально: сохраняем refresh token в HttpOnly cookie через backend
            // или также в localStorage (с учетом рисков)
        }
        return data;
    },

    getAuthHeader() {
        const token = localStorage.getItem('accessToken');
        return token ? { 'Authorization': `Bearer ${token}` } : {};
    },

    async makeAuthenticatedRequest(url, options = {}) {
        const headers = { ...options.headers, ...this.getAuthHeader() };
        return fetch(`${API_URL}${url}`, { ...options, headers });
    },

    logout() {
        localStorage.removeItem('accessToken');
        // Дополнительный запрос на инвалидацию токена на сервере
    }
};

Когда cookies всё же предпочтительнее?

Несмотря на перечисленные преимущества, cookies с флагом HttpOnly остаются лучшим выбором для:

  • Refresh токенов (для снижения риска XSS-атак).
  • Сессионной аутентификации в традиционных server-rendered приложениях (например, на Express.js с сессиями).
  • Сценариев, требующих высокой защиты от XSS, если приложение не может гарантировать их отсутствие (хотя это антипаттерн — безопасность не должна строиться на этом).

Итог

Основной компромисс — безопасность против удобства.

  • localStorage/sessionStorage + заголовки: Защита от CSRF, но уязвимость к XSS (злоумышленник может украсть токен через внедренный скрипт). Требует тщательной защиты от XSS (санитизация, CSP).
  • HttpOnly cookies: Защита от XSS (токен недоступен из JavaScript), но уязвимость к CSRF (требует дополнительных мер).

В современных SPA с надежной защитой от XSS (Content Security Policy, избегание innerHTML, использование фреймворков с экранированием) хранение access token в localStorage с передачей через заголовок становится стандартом de facto. Это обеспечивает лучший UX (контроль над запросами), упрощает архитектуру и соответствует принципам REST. Однако refresh token рекомендуется хранить в HttpOnly cookie (устанавливаемой сервером) для баланса безопасности и функциональности.

Почему сохраняешь токен не в cookie? | PrepBro