← Назад до блогу

JWT Refresh Token Rotation: безпечний шаблон для реальних систем

Безпека розробника28 березня 2026·9 хв прочитати
JWT refresh token rotation diagram

Більшість навчальних посібників JWT зупиняються на «використовуйте маркер оновлення для довготривалих сеансів». Вони рідко пояснюють, що відбувається, коли цей маркер оновлення вкрадено. Статичний токен оновлення, який ніколи не змінюється, надає зловмиснику такий же постійний доступ, як і викрадений файл cookie сеансу. Гірше того, неможливо виявити крадіжку.

Обертання маркерів оновлення вирішує цю проблему, видаючи новий маркер оновлення під час кожного використання. Якщо старий маркер відтворюється повторно, весь сеанс скасовується. У цій статті описано шаблон реалізації, який використовують Auth0, Okta та інші серйозні постачальники ідентифікаційної інформації.

Чому не вдається статичне оновлення маркерів

Статичний маркер оновлення має фіксоване значення протягом усього терміну дії сеансу (часто 30+ днів). Проблеми:

Виправлення полягає в тому, щоб розглядати кожен маркер оновлення як одноразовий.

Модель обертання

Крок 1: Вхід — Створіть сімейство токенів

Коли користувач автентифікується, згенеруйте family_id (UUID), який групує всі маркери в цьому сеансі:

{ family_id: "f47ac10b-58cc-4372-a567-0e02b2c3d479", user_id: "user_123", refresh_token_hash: sha256(refresh_token), created_at: "2026-03-28T10:00:00Z", expires_at: "2026-04-27T10:00:00Z", used: false
}

Зберігати цей запис на сервері. Ніколи не зберігайте маркери оновлення в localStorage — використовуйте httpТільки файли cookie або захищене власне сховище.

Крок 2: Оновлення маркера — обертання

Коли клієнт представляє маркер оновлення:

  1. Знайдіть хеш маркера в базі даних
  2. Переконайтеся, що він належить до активної родини та не позначений як used
  3. Позначити як used: true
  4. Видайте новий маркер оновлення (нове випадкове значення, те саме family_id)
  5. Видайте новий маркер доступу
  6. Зберігати новий хеш маркера оновлення

Крок 3: Виявлення повтору

Якщо маркер оновлення, позначений як used: true, відображається знову, це означає:

Безпечна відповідь: відкликати всі маркери в родині. Це змушує користувача знову увійти. Краще один раз завдати незручностей законному користувачеві, ніж дозволити зловмиснику зберегти доступ.

// Виявлено повтор — ядерний варіант
очікувати db.query( "ВИДАЛИТИ З refresh_tokens WHERE family_id = $1", [familyId]
);

Схема бази даних

Мінімальна таблиця маркерів оновлення:

СТВОРИТИ ТАБЛИЦЮ refresh_tokens ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), family_id UUID NOT NULL, user_id UUID NOT NULL REFERENCES users(id), token_hash TEXT NOT NULL UNIQUE, використовується BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ DEFAULT зараз(), expires_at TIMESTAMPTZ NOT NULL, replaced_by UUID ПОСИЛАННЯ refresh_tokens(id)
); CREATE INDEX idx_refresh_family ON refresh_tokens(family_id);
CREATE INDEX idx_refresh_user ON refresh_tokens(user_id);

Стовпець replaced_by створює ланцюжок, за яким можна стежити для перевірки.

Обробка одночасних запитів

У реальних програмах кілька викликів API можуть ініціювати оновлення одночасно. Якщо Виклик A повертає маркер, а Виклик B все ще зберігає старий, Виклик B запускає виявлення повтору — несподіваний вихід користувача з системи.

Шаблон пільгового періоду

Дозволити попередньому маркеру оновлення залишатися дійсним протягом короткого вікна (5–10 секунд) після ротації:

const GRACE_PERIOD_MS = 10_000; if (token.used) { const elapsed = Date.now() - token.rotated_at; якщо (минуло < GRACE_PERIOD_MS) { // Within grace period — return the already-issued new tokens return getLatestTokensForFamily(token.family_id); } // Outside grace period — replay attack await revokeFamily(token.family_id); throw new UnauthorizedError('token_replayed');
}

Моніторинг і сповіщення

Відстежуйте ці сигнали у виробництві:

Маркер оновлення проти маркера доступу: короткий довідник

PropertyAccess TokenRefresh Token
Тривалість5–15 хвилин7–30 днів
ФорматJWT (самодостатній)Непрозорий рядок (рекомендовано)
ЗберіганняПам'ять (змінна JS)httpЛише cookie
Надіслано наAPI-сервериЛише сервер авторизації
ОбертанняНе потрібноКожне використання

Про помилки перевірки вимоги часу, які часто взаємодіють із потоками оновлення, див.

FAQ

Як обробляти одночасні запити на оновлення?

Використовуйте короткий пільговий період (5–10 секунд), коли попередній маркер оновлення все ще приймається. Це обробляє умови змагання, коли кілька викликів API запускають оновлення одночасно. Після пільгового періоду дійсним буде лише найновіший маркер.

Чи мають маркери оновлення бути JWT або непрозорими рядками?

Непрозорі рядки, як правило, безпечніші для маркерів оновлення. JWT несуть корисні дані, які збільшують поверхню атаки, і маркери оновлення не повинні бути автономними, оскільки сервер завжди шукає їх у базі даних.

Як довго мають оновлювати сеанси?

7–30 днів зазвичай. Додатки з високим рівнем безпеки (банківська справа, охорона здоров’я) мають використовувати 1–7 днів. Споживчі програми можуть працювати до 90 днів із ротацією. Завжди поєднуйте довгі сеанси з відбитком пальців пристрою та виявленням аномалій.

Пов’язані інструменти та статті