Срок жизни Cookie и синхронизация с ttl токена

При работе с библиотекой @hapi/iron важно понимать, что сама она не управляет cookie напрямую, а занимается шифрованием и подписанием данных, которые чаще всего помещаются в cookie. Поэтому срок жизни cookie определяется на уровне HTTP и логики приложения, а не самим Iron. Однако именно связка Iron + cookie + токен формирует единый механизм контроля сессий.

Базовые принципы TTL (Time To Live)

TTL задаёт время жизни данных, после которого они считаются недействительными. В системах с токенами и cookie обычно присутствуют два уровня TTL:

  • TTL cookie (HTTP-уровень)
  • TTL токена (логический уровень приложения)

Несоответствие этих значений приводит к типичным ошибкам:

  • cookie существует, но токен внутри уже истёк
  • токен валиден, но cookie удалена браузером
  • возможна рассинхронизация состояния сессии

Iron используется для упаковки структуры данных в зашифрованную строку:

  • данные сериализуются
  • применяется шифрование и подпись
  • результат помещается в cookie

При этом библиотека может хранить внутри защищённого payload:

  • userId
  • sessionId
  • accessToken
  • expiry timestamp
  • дополнительные метаданные

Сам Iron не «знает», что такое TTL, но позволяет включать его в полезную нагрузку, например как поле exp.

TTL токена как источник истины

В современных архитектурах TTL токена часто считается главным источником контроля сессии.

Пример структуры данных внутри Iron-сообщения:

{
  "userId": 42,
  "token": "eyJhbGciOi...",
  "exp": 1735689600
}

Поле exp обычно хранит UNIX timestamp окончания жизни токена.

При каждом запросе происходит:

  • извлечение cookie
  • дешифровка через Iron
  • проверка текущего времени против exp

Если токен истёк — сессия считается недействительной независимо от cookie TTL.

Cookie имеет собственные параметры:

  • Max-Age
  • Expires
  • HttpOnly
  • Secure
  • SameSite

TTL cookie влияет на транспортный уровень:

  • браузер удаляет cookie после истечения времени
  • сервер перестаёт получать данные автоматически

Однако cookie TTL не гарантирует валидность токена внутри.

Типичная проблема возникает, когда:

  • cookie живёт 7 дней
  • токен внутри Iron живёт 15 минут

Результат:

  • пользователь отправляет cookie
  • сервер успешно его расшифровывает
  • внутри обнаруживается истёкший токен
  • сессия отклоняется

Обратная ситуация:

  • токен живёт дольше cookie
  • браузер удаляет cookie раньше
  • пользователь неожиданно выходит из системы

Синхронизация TTL: базовая модель

Корректная архитектура предполагает единый источник времени жизни:

TTL токена должен определять TTL cookie

Это означает:

  • если токен истекает через 15 минут
  • cookie тоже должна истекать через 15 минут

Пример согласованной настройки

const ttl = 15 * 60 * 1000; // 15 минут

const cookieOptions = {
  maxAge: ttl,
  httpOnly: true,
  secure: true,
  sameSite: 'lax'
};

const payload = await Iron.seal(
  {
    userId: 42,
    exp: Date.now() + ttl
  },
  password,
  Iron.defaults
);

Ключевой момент — TTL задаётся один раз и используется в двух местах:

  • cookie maxAge
  • поле exp внутри Iron payload

Стратегии синхронизации TTL

1. Жёсткая синхронизация

TTL токена = TTL cookie

Используется в системах:

  • с короткими сессиями
  • без refresh token механизма

Плюсы:

  • простота
  • предсказуемость

Минусы:

  • частые перелогины

2. Sliding session (скользящий TTL)

TTL обновляется при каждом запросе:

  • каждый запрос продлевает cookie
  • пересоздаётся Iron payload

Логика:

запрос → проверка → обновление exp → перезапись cookie

Плюсы:

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

Минусы:

  • сложнее реализация
  • нагрузка на сервер

3. Разделённый TTL (access + refresh)

Используются два уровня:

  • access token (короткий TTL)
  • refresh token (длинный TTL)

В Iron обычно хранится:

  • access token
  • время истечения

Cookie синхронизируется с refresh механизмом.

Проверка TTL при расшифровке Iron

После вызова Iron.unseal обязательно выполняется проверка:

const data = await Iron.unseal(sealed, password, Iron.defaults);

if (data.exp < Date.now()) {
  throw new Error('Session expired');
}

Это критический слой безопасности, так как:

  • зашифрованная строка может быть валидной
  • но содержимое уже устарело

Типичные ошибки синхронизации

1. Разные источники времени

  • cookie основана на server time
  • token основан на client time

Решение:

  • всегда использовать server time

2. Отсутствие обновления exp

  • cookie обновляется
  • token exp остаётся старым

Результат:

  • рассинхронизация при активной сессии

3. Игнорирование clock skew

Если серверы распределены:

  • возможна разница времени
  • TTL может «сгорать» раньше

Решение:

  • ввод буфера (например, -30 секунд)

Рекомендованная модель жизненного цикла

  1. Пользователь логинится

  2. Генерируется токен с exp

  3. Создаётся Iron payload

  4. Устанавливается cookie с maxAge = exp - now

  5. При каждом запросе:

    • проверка Iron
    • проверка exp
  6. При необходимости:

    • обновление exp
    • перезапись cookie

Поведение при истечении TTL

Когда TTL истекает:

  • Iron payload становится логически недействительным
  • сервер отклоняет сессию
  • cookie может быть удалена или проигнорирована

Важно: даже корректно расшифрованные данные не считаются валидными без проверки TTL.

Безопасностные аспекты TTL

TTL напрямую влияет на:

  • окно атаки при утечке cookie
  • длительность несанкционированного доступа
  • устойчивость к replay-атакам

Короткий TTL:

  • уменьшает риск компрометации
  • увеличивает нагрузку на авторизацию

Длинный TTL:

  • удобен пользователю
  • увеличивает поверхность атаки

Баланс определяется типом системы.

Практическая модель согласования

Единый конфиг TTL:

const SESSION_TTL = 30 * 60 * 1000;

const session = {
  exp: Date.now() + SESSION_TTL
};

const cookie = {
  maxAge: SESSION_TTL
};

Iron используется только как транспорт:

  • не источник TTL
  • не механизм контроля времени
  • только защита данных

Поведение при обновлении токена

При refresh сценарии:

  • старый Iron payload аннулируется
  • создаётся новый exp
  • cookie перезаписывается

Важно избегать:

  • параллельных валидных сессий
  • частичного обновления TTL

Итоговая модель взаимодействия

  • TTL токена — логический контроль жизни сессии
  • TTL cookie — транспортный контроль хранения
  • Iron — механизм защиты и целостности данных
  • синхронизация TTL обязательна для предсказуемого поведения системы