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

JWT — exp, iat та nbf — оманливо прості. Це просто мітки часу Unix. Проте вони спричиняють деякі з найбільш неприємних помилок автентифікації у виробництві: маркери, термін дії яких закінчується надто рано, маркери, які працюють на одному сервері, але не на іншому, і маркери, термін дії яких взагалі не закінчується.
Ця стаття охоплює найпоширеніші помилки, пов’язані з тимчасовими вимогами, чому вони виникають і як їх виправити за допомогою безпечних параметрів за замовчуванням.
Як працюють exp, iat і nbf
Кожен JWT може містити три вимоги, пов’язані з часом, у своєму корисному навантаженні:
- exp (термін дії) — маркер НЕ ПОВИНЕН прийматися після цієї позначки часу
- iat (Видано) — коли було створено маркер
- nbf (Не раніше) — маркер НЕ ПОВИНЕН бути прийнятий до цієї позначки часу
Усі три є числовими датами: кількість секунд з 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, який проходить перевірку часу, але має недійсний підпис.
Порядок безпечної перевірки:
- Декодувати заголовок (перевірити алгоритм)
- Підтвердити підпис
- Перевірити
exp(відхилити, якщо термін дії минув) - Перевірити
nbf(відхилити, якщо ще недійсний) - Перевірити
iat(відхилити, якщо невиправдано старий) - Емітент чека, аудиторія та інші претензії
Більшість добре підтримуваних бібліотек обробляють це правильно, але спеціальне проміжне програмне забезпечення часто помиляється.
Помилка №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 днів для маркера доступу перешкоджає меті короткочасних маркерів. Рекомендації:
- Маркети доступу: 5–15 хвилин
- Оновити токени: 7–30 днів (з ротацією)
- ID токенів: 1 година
Щоб отримати безпечні шаблони маркерів оновлення, перегляньте наш посібник із обертання маркерів оновлення JWT.
Тестові випадки, які повинен мати кожен API
Додайте це до свого набору тестів, щоб завчасно виявляти помилки, пов’язані з вимогою часу:
- Токен із
expв минулому → 401 - Токен з
expточно зараз → 401 (межа) - Токен без
exp→ 401 - Токен з
nbfв майбутньому → 401 - Токен з
nbfтрохи в майбутньому (в межах допустимого відхилення) → 200 - Токен з
iatу майбутньому → 401 (вказує на втручання) - Токен із мітками часу в мілісекундах → 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 — це означає, що маркер дійсний, але не має дозволів, що є іншою ситуацією.
Пов’язані інструменти та статті
- JWT Decoder — перевірте заявки часу в будь-якому токені
- Помилки безпеки JWT — ширші підводні камені JWT поза межами претензій
- Оновити ротацію маркерів — безпечний шаблон для тривалих сеансів
- 2FA Guide — додайте другий рівень поза маркерами