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

Помилки JWT exp/iat/nbf: помилки, пов’язані з вимогою часу, які порушують автентифікацію

Безпека розробника27 березня 2026·8 хвилин читання
JWT time claims debugging
Заявки на час

JWT — exp, iat та nbf — оманливо прості. Це просто мітки часу Unix. Проте вони спричиняють деякі з найбільш неприємних помилок автентифікації у виробництві: маркери, термін дії яких закінчується надто рано, маркери, які працюють на одному сервері, але не на іншому, і маркери, термін дії яких взагалі не закінчується.

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

Як працюють exp, iat і nbf

Кожен JWT може містити три вимоги, пов’язані з часом, у своєму корисному навантаженні:

Усі три є числовими датами: кількість секунд з 1970-01-01T00:00:00Z (епоха Unix). Не мілісекунди — секунди. Саме ця відмінність спричиняє приблизно 20% усіх помилок часу JWT.

Використовуйте наш JWT Decoder, щоб миттєво перевірити заявки на час у будь-якому токені.

Помилка №1: використання мілісекунд замість секунд

JavaScript Date.now() повертає мілісекунди. Для специфікації JWT потрібні секунди. Встановлення exp на Date.now() + 3600000 створює маркер, термін дії якого закінчується в 2089 році, а не через одну годину.

// НЕПРАВИЛЬНО — мілісекунди
const exp = Date.now() + 3600000; // ПРАВИЛЬНО — секунди
const exp = Math.floor(Date.now() / 1000) + 3600;

Більшість бібліотек JWT обробляють це внутрішньо, але якщо ви створюєте корисні дані вручну, це перше, що потрібно перевірити.

Помилка №2: повністю відсутня вимога exp

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

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

// Node.js jsonwebtoken — примусове закінчення терміну дії
jwt.verify(токен, секрет, { maxAge: '1h' });

Помилка №3: перекіс годинника між серверами

Сервер A видає маркер о 14:00:00. Годинник сервера B показує 13:59:55 (5 секунд позаду). Якщо маркер має nbf: 1711540800 (14:00:00), сервер B відхиляє його як «ще недійсний».

Це особливо часто зустрічається в:

Виправлення

Дозвольте невеликий допуск годинника (також званий «вільним ходом») — зазвичай 30–60 секунд:

// jsonwebtoken
jwt.verify(токен, секрет, { clockTolerance: 30 }); // jose (Node.js)
очікувати jwtVerify(токен, ключ, { clockTolerance: '30s' });

Ніколи не встановлюйте допустиме значення вище 2 хвилин. Якщо вам потрібно більше, натомість виправте синхронізацію NTP.

Помилка №4: неправильний порядок перевірки

Правильний порядок перевірки має значення. Якщо ви перевірите підпис після завершення перевірки, зловмисник може створити маркер із майбутнім exp, який проходить перевірку часу, але має недійсний підпис.

Порядок безпечної перевірки:

  1. Декодувати заголовок (перевірити алгоритм)
  2. Підтвердити підпис
  3. Перевірити exp (відхилити, якщо термін дії минув)
  4. Перевірити nbf (відхилити, якщо ще недійсний)
  5. Перевірити iat (відхилити, якщо невиправдано старий)
  6. Емітент чека, аудиторія та інші претензії

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

Помилка №5: плутанина часового поясу в iat

JWT – це завжди UTC. Але розробники іноді створюють їх за місцевим часом:

// НЕПРАВИЛЬНО — місцевий часовий пояс
const iat = нова дата ('2026-03-27T14:00:00').getTime() / 1000; // ПРАВИЛЬНО — явне UTC
const iat = нова дата ('2026-03-27T14:00:00Z').getTime() / 1000;

Без суфікса Z JavaScript інтерпретує рядок у місцевому часовому поясі, що може зміщувати мітку часу на години.

Помилка №6: Прийняття токенів без перевірки nbf

Заявка nbf корисна для маркерів відкладеної активації — наприклад, маркера, який має працювати лише після запланованого розгортання. Якщо ваш валідатор ігнорує nbf, ці маркери можна використовувати до запланованого часу активації.

Більшість бібліотек перевіряють nbf за замовчуванням, але перевірте це в налаштуваннях, особливо для спеціального проміжного ПЗ.

Помилка №7: надто довгий термін дії

Установлення exp на 30 днів для маркера доступу перешкоджає меті короткочасних маркерів. Рекомендації:

Щоб отримати безпечні шаблони маркерів оновлення, перегляньте наш посібник із обертання маркерів оновлення JWT.

Тестові випадки, які повинен мати кожен API

Додайте це до свого набору тестів, щоб завчасно виявляти помилки, пов’язані з вимогою часу:

  1. Токен із exp в минулому → 401
  2. Токен з exp точно зараз → 401 (межа)
  3. Токен без exp → 401
  4. Токен з nbf в майбутньому → 401
  5. Токен з nbf трохи в майбутньому (в межах допустимого відхилення) → 200
  6. Токен з iat у майбутньому → 401 (вказує на втручання)
  7. Токен із мітками часу в мілісекундах → 401 (виявляє помилку мс/с)

Безпечні параметри за замовчуванням для популярних фреймворків

Node.js (jsonwebtoken)

jwt.sign(корисне навантаження, секрет, { expiresIn: '15m' });
jwt.verify(токен, секрет, { clockTolerance: 30, maxAge: '15m'
});

Python (PyJWT)

jwt.decode(токен, ключ, алгоритми=['HS256'], свобода дій=timedelta(секунди=30), параметри={'require': ['exp', 'iat']})

Go (golang-jwt)

парсер := jwt.NewParser( jwt.WithLeeway(30 * час.Секунда), jwt.WithValidMethods([]string{"HS256"}),
)

FAQ

Це має бути обов’язковим?

Хоча в специфікації JWT сказано, що iat є необов’язковим, його обов’язковість допомагає з налагодженням і журналами аудиту. Без iat ви не можете визначити, коли було створено маркер, що ускладнює кореляцію з подіями безпеки.

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

Звичайне безпечне значення за умовчанням становить 30–60 секунд. Більше 2 хвилин становить загрозу безпеці. Якщо вашій системі потрібно більше, виправте синхронізацію NTP замість розширення дозволу на перекос.

Який код статусу HTTP я маю повернути для простроченого маркера?

Повернути 401 Неавторизований з чітким текстом помилки, наприклад {"error": "token_expired"}. Не повертати 403 Forbidden — це означає, що маркер дійсний, але не має дозволів, що є іншою ситуацією.

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