Токен аутентификации — это ключевой объект безопасности, который фактически заменяет пароль в процессе работы приложения. Любая утечка токена автоматически превращается в компрометацию сессии пользователя, а в некоторых случаях — в полный доступ к аккаунту без необходимости обхода дополнительных проверок.
1. XSS (Cross-Site Scripting) Один из самых распространённых сценариев. Если приложение допускает внедрение произвольного JavaScript-кода, атакующий получает возможность:
localStorage или
sessionStorageОсобенно опасно хранение токенов в localStorage, так как
он полностью доступен из JavaScript-контекста.
2. Утечка через HTTP и отсутствие TLS При передаче токена без HTTPS он может быть перехвачен:
Даже один запрос без TLS делает всю систему уязвимой.
3. Логи серверов и клиентских прокси Токены часто случайно попадают в:
Это особенно критично при отсутствии маскирования заголовков
Authorization.
4. Утечка через Referer Если токен попадает в URL
(например, как query parameter), он может быть отправлен в заголовке
Referer на сторонние сайты при переходе по ссылке.
5. Браузерные расширения и вредоносное ПО Расширения с доступом к DOM могут считывать содержимое страниц, включая токены, даже если приложение защищено на уровне API.
6. Ошибки архитектуры хранения Типичные ошибки:
localStorageБиблиотека Iron (чаще всего используемая через экосистему Node.js, особенно в связке с Hapi) предназначена не просто для хранения данных, а для их криптографического “запечатывания” (sealing).
Основная идея — токен не хранится в открытом виде даже на стороне клиента или в промежуточных слоях. Вместо этого используется зашифрованная структура, которую можно безопасно передавать.
Iron работает по принципу:
Обратная операция называется unseal — восстановление
данных при наличии корректного ключа.
import Iron from '@hapi/iron';
const password = 'super-secure-password';
const tokenData = {
userId: 123,
role: 'admin'
};
// Шифрование (seal)
const sealedToken = await Iron.seal(tokenData, password, Iron.defaults);
// Расшифровка (unseal)
const unsealed = await Iron.unseal(sealedToken, password, Iron.defaults);
Важный момент: без password расшифровать данные
невозможно, даже если токен перехвачен.
Один из наиболее безопасных подходов — хранение sealed-токена в cookie:
HttpOnly — недоступен из JavaScriptSecure — передаётся только по HTTPSSameSite=Strict — защита от CSRFПример логики:
Set-Cookie: session=sealed_token; HttpOnly; Secure; SameSite=Strict
Даже если атакующий получит доступ к фронтенду, он не сможет прочитать токен через JS.
Iron не устраняет XSS, но резко снижает его последствия:
Iron поддерживает TTL:
const sealed = await Iron.seal(data, password, {
...Iron.defaults,
ttl: 15 * 60 * 1000
});
Даже если токен утёк, окно атаки ограничено.
При использовании Iron важно регулярно менять секрет:
Практика:
В зашифрованные данные можно включать:
const payload = {
userId: 123,
ip: '192.168.0.1',
ua: 'Mozilla/5.0'
};
При расшифровке выполняется проверка соответствия контексту.
Если секрет одинаков для всех окружений (dev, stage, prod), компрометация одного окружения ломает всю систему.
Даже зашифрованный токен теряет смысл, если он передаётся:
Iron защищает данные, но не отменяет необходимость:
Ошибкой является помещение внутрь:
Токен должен содержать только идентификаторы и минимальный контекст.
Если sealed-токен попадает к атакующему:
Таким образом модель угроз смещается с “кражи токена” на:
Правильная система строится не вокруг одного инструмента, а вокруг слоя защиты:
JWT:
Iron:
Это делает Iron более подходящим там, где важна именно конфиденциальность содержимого токена, а не только его подпись.
Типовая схема:
Сервер создаёт session payload
Payload шифруется через Iron
Результат сохраняется в HttpOnly cookie
Каждый запрос:
При нарушении условий токен инвалидируется
На практике Iron работает как часть многослойной системы:
Каждый слой снижает вероятность успешной эксплуатации утечки, даже если один из них будет нарушен.