Разработка программного обеспечения
Что такое JWT? Веб-токены JSON на рабочем примере
JWT позволяет серверу доверять запросу, ничего не проверяя. Создайте его с нуля, попытайтесь внести в него изменения и наблюдайте за его сбоем, а затем узнайте, когда токены превосходят сеансы и как их безопасно использовать.
- JWT — это компактный подписанный токен, состоящий из трех частей: заголовка, полезных данных и подписи.
- Подпись доказывает, что токен не был изменен, поэтому сервер может доверять ему без поиска.
- Полезная нагрузка доступна для чтения любому, поэтому никогда не помещайте в нее секреты.
- Токены трудно отозвать, поэтому держите их недолговечными и проверяйте их с помощью доверенной библиотеки.
Как сервер узнает после того, как вы вошли в систему, что каждый последующий запрос действительно исходит от вас? Одним из популярных ответов является JSON Web Token или JWT, обычно произносится как «jot». Это небольшой подписанный фрагмент данных, который клиент отправляет с каждым запросом, и сервер может проверить его, не затрагивая базу данных.
Самый быстрый способ понять JWT — создать его, а затем попытаться сломать. Мы сделали это, используя только стандартную библиотеку Python для Python 3.13, и приведенный ниже результат получен в результате этого запуска. Этот код предназначен для обучения. В реальном приложении используйте хорошо поддерживаемую библиотеку, поскольку в деталях проверки легко ошибиться.
Что такое JWT?
JWT — это открытый стандарт, определенный в RFC 7519, для передачи утверждений между двумя сторонами в виде компактной строки. Утверждение — это просто утверждение, например «Идентификатор этого пользователя — 42». Поскольку токен подписан, получатель может подтвердить, кто его выдал и что никто его не изменял.
Подумайте о фестивальном браслете. Сотрудники у ворот один раз проверят ваш билет и выдадут вам группу, которую трудно подделать. После этого каждый ларек просто смотрит на группу, без необходимости звонить в кассу. JWT работает почти так же, как и API.
Как работает вход в JWT
- Пользователь отправляет имя пользователя и пароль на конечную точку входа.
- Сервер проверяет их и создает подписанный токен.
- Клиент сохраняет токен.
- Каждый последующий запрос включает его, обычно в заголовке:
Authorization: Bearer <token>. - Сервер проверяет подпись и срок действия, затем считывает утверждения.
Основное преимущество заключается в шаге 5. Серверу не нужно искать сеанс, что удобно, когда многим службам или серверам необходимо проверить один и тот же токен. Если вы новичок в API, начните с что такое API.
Три части JWT
JWT выглядит как произвольный текст, но это три раздела в кодировке Base64URL, разделенные точками. Вот один из них, который мы создали:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiI0MiIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzkzNTAwMDAwLCJleHAiOjE3OTM1MDA5MDB9
.7YfUDH_T-iDh8bMmUT8mmKYwOstzT8Ty--DTTAmQGWM
Мы добавили разрывы строк, чтобы было легче читать. Расшифровка первых двух частей дает простой JSON:
Header: {"alg":"HS256","typ":"JWT"}
Payload: {"sub":"42","role":"user","iat":1793500000,"exp":1793500900}
| Часть | Содержит | Цель |
|---|---|---|
| Заголовок | Тип токена и алгоритм подписи | Говорит, как проверить токен |
| Полезная нагрузка | Претензии к пользователю или сеансу | Актуальная информация |
| Подпись | Хэш первых двух частей с использованием ключа | Доказывает целостность и происхождение |
Стандартные претензии
| Претензия | Имя | Значение |
|---|---|---|
суб |
Тема | Для кого предназначен токен, обычно это идентификатор пользователя. |
это |
Эмитент | Кто создал токен |
аудит |
Аудитория | Для кого предназначен токен |
опыт |
Срок действия | Когда токен перестает быть действительным |
там |
Выпущено в | Когда он был создан |
НБФ |
Не раньше | Когда он начнет действовать |
Джти |
JWT-идентификатор | Уникальный идентификатор для этого токена |
Вы также можете добавить собственные утверждения, например role. Время — это временные метки Unix в секундах. Полезная нагрузка должна быть небольшой, поскольку токен передается при каждом запросе.
Построй сам
Создание токена включает в себя кодирование двух частей JSON, а затем их подписание секретом с использованием HMAC-SHA256:
import base64, hashlib, hmac, json
SECRET = b"demo-secret-do-not-use-in-production"
def b64url(data):
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
def sign(message):
return b64url(hmac.new(SECRET, message.encode(), hashlib.sha256).digest())
def create_token(payload):
header = {"alg": "HS256", "typ": "JWT"}
parts = [
b64url(json.dumps(header, separators=(",", ":")).encode()),
b64url(json.dumps(payload, separators=(",", ":")).encode()),
]
message = ".".join(parts)
return message + "." + sign(message)
Проверка работает путем пересчета подписи и сравнения ее с подписью в токене. Только тот, у кого есть секрет, может создать соответствующий:
def verify_token(token, now):
header, payload, signature = token.split(".")
expected = sign(header + "." + payload)
if not hmac.compare_digest(signature, expected):
raise ValueError("bad signature")
claims = json.loads(b64url_decode(payload))
if claims["exp"] < now:
raise ValueError("token expired")
return claims
Действительный токен вернул свои утверждения, как и ожидалось. compare_digest используется, поскольку он выполняется в постоянное время, что позволяет избежать утечки информации из-за разницы во времени.
Попробуйте вмешаться в это
Предположим, злоумышленник декодирует полезную нагрузку, меняет "role":"user" на "role":"admin", снова кодирует ее и сохраняет старую подпись. Мы попробовали это вместе с двумя другими атаками:
| Попытка | Результат |
|---|---|
| Изменил роль на администратора | плохая подпись |
| Использовал токен через 1 секунду после истечения срока действия. | срок действия токена истек |
Установите "alg":"none" и удалите подпись |
плохая подпись |
Эта последняя попытка нацелена на реальную историческую слабость. Некоторые библиотеки когда-то доверяли собственному заголовку токена, поэтому злоумышленник мог просто заявить, что подпись не требуется. Наш код всегда использует один фиксированный алгоритм, поэтому он был отклонен. В реальной библиотеке всегда указывайте принимаемые вами алгоритмы.
Подписано, не зашифровано
Это наиболее неправильно понимаемый момент в JWT. Наш поддельный токен был отклонен, однако его содержимое по-прежнему было прекрасно читаемо:
{"sub":"42","role":"admin","iat":1793500000,"exp":1793500900}
Base64URL — это кодировка, а не шифрование, и любой может отменить его вообще без ключа. Подпись не позволяет людям изменять данные, но не читать их. Поэтому никогда не помещайте пароли, номера карт или личные данные в полезные данные JWT. Если содержимое необходимо скрыть, используйте зашифрованные токены (JWE) или просто храните данные на сервере.
HS256 против RS256
| Аспект | ХС256 | RS256 или ES256 |
|---|---|---|
| Тип | Симметричный | Асимметричный |
| Ключи | Один общий секрет | Закрытый ключ подписывает, открытый ключ проверяет |
| Кто может создавать токены | Любой, кто хранит секрет | Только владелец закрытого ключа |
| Лучшее для | Один сервис или небольшая доверенная группа | Множество сервисов, третьих лиц, поставщиков удостоверений |
Благодаря асимметричной подписи другие службы могут проверять токены, не имея возможности чеканить свои собственные, что ограничивает ущерб в случае взлома одного из них.
JWT против сессий
JWT часто представляют как современную замену сеансов. На самом деле у каждого есть компромиссы:
| Аспект | Сеанс сервера | JWT |
|---|---|---|
| Где живет государство | На сервере | В токене |
| Поиск по каждому запросу | Обычно | Не требуется |
| Выход из системы или отзыв | Легко, удалите сеанс | Твердо до истечения срока действия |
| Размер по запросу | Малый идентификатор | Токен большего размера (у нас было 161 символ) |
| Во многих сервисах | Требуется общее хранилище | Каждый может убедиться в этом напрямую |
| Изменение разрешений | Применяется немедленно | Старый токен остается действительным до истечения срока действия. |
Для традиционного веб-сайта, отображаемого на сервере, сеансовый файл cookie часто проще и безопаснее. JWT, как правило, имеют больше смысла для API, используемых несколькими службами, или для мобильных клиентов.
Совет: Не выбирайте JWT только потому, что это звучит без сохранения состояния. Многие реальные системы добавляют список запретов или проверку базы данных для поддержки выхода из системы, что возвращает поиск, которого JWT должен был избегать.
Где хранить токены в браузере
| Расположение | XSS-риск | CSRF-риск | Примечания |
|---|---|---|---|
локальное хранилище |
Высокий, поскольку скрипты могут его прочитать | Низкий | Простой, но доступный любому внедренному скрипту |
| HttpOnly cookie | Ниже, так как скрипты не могут его прочитать. | Нужна защита | Используйте Secure и SameSite |
| Переменная в памяти | Нижний | Низкий | Потеряно при обновлении, поэтому требуется поток обновления |
Многие руководства по безопасности отдают предпочтение файлам cookie HttpOnly, Secure, SameSite для веб-приложений. Какой бы вариант вы ни выбрали, всегда используйте HTTPS. Чтобы отправить токен из JavaScript, ознакомьтесь с нашим руководством по API JavaScript Fetch.
Обновить токены
Короткий срок действия ограничивает ущерб от украденного токена, но никто не хочет входить в систему каждые 15 минут. Обычное решение использует два токена:
- Токен доступа: действует недолго, например несколько минут, и отправляется с каждым запросом API.
- Токен обновления: хранится дольше, хранится более бережно и используется только для получения новых токенов доступа.
Сервер отслеживает токены обновления и может отозвать их, что дает вам практичный выход из системы. Их ротация при каждом использовании также помогает обнаружить кражу, поскольку украденный старый жетон воспроизводится заново.
Распространенные ошибки безопасности
- Декодирование без проверки. Чтение полезных данных не делает их заслуживающими доверия. Всегда проверяйте подпись.
- Принятие любого алгоритма. Установите явный список разрешенных алгоритмов.
- Использование слабого секрета. Короткие секреты можно взломать в автономном режиме. Используйте длинное случайное значение и держите его вне контроля версий.
- Пропуск срока действия. Токены без
expмогут работать вечно, если их украдут. - Не проверять
issиaud. В противном случае токен, выданный для одного сервиса, может быть принят другим. - Доверие к устаревшим утверждениям. Роль внутри токена может быть устаревшей до истечения срока ее действия.
- Токены журналирования. Любой, кто читает ваши журналы, может использовать их повторно.
- Не проверяем пути сбоя. Добавьте тесты для просроченных и измененных токенов, а также действительных. Наше руководство по тестированию API с помощью Postman может помочь.
Итоги
JWT — это подписанные данные, которым сервер может доверять без поиска, а не секретный контейнер и не автоматическое обновление через сеансы. Используйте короткий срок действия, проверяйте все с помощью поддерживаемой библиотеки и не допускайте попадания личных данных в полезную нагрузку. Чтобы убедиться в этом сами, запустите небольшой скрипт, подобный нашему, измените один символ токена и увидите, что проверка не удалась.
Часто задаваемые вопросы
Для чего используется JWT?
JWT в основном используются для аутентификации и авторизации в API. После входа в систему клиент отправляет токен с каждым запросом, а сервер проверяет его, чтобы идентифицировать пользователя.
Зашифрован ли JWT?
Обычно нет. Стандартный JWT подписан и закодирован, поэтому любой может прочитать его полезную информацию. Подпись только предотвращает изменения. Зашифрованные токены используют родственный формат под названием JWE.
Как долго должен длиться JWT?
Токены доступа обычно недолговечны, часто это минуты, а не дни. Соедините их с токеном обновления, чтобы пользователям не приходилось постоянно входить в систему.
Можно ли отозвать JWT?
Не сам по себе, поскольку он остается действительным до истечения срока его действия. Для отзыва требуется отслеживание на стороне сервера, например список запретов, который лишает некоторых преимуществ отсутствия сохранения состояния.
JWT лучше сеансов?
Ни то, ни другое не всегда лучше. Сеансы проще отозвать, и они подходят для многих веб-сайтов, в то время как JWT подходят для API и сервисов, которым необходимо независимо проверять личность.
Продолжайте учиться
Нужна помощь с тестированием, WordPress или SEO?
Мы помогаем командам выпускать лучшее программное обеспечение и развивать свои сайты. Расскажите нам, над чем вы работаете, и мы предложим четкий следующий шаг.
Больше руководств по тестирование, искусственный интеллект и современные разработки
Понравилась эта статья? Выберите тему и сразу приступайте к нашим новейшим руководствам.
Объяснение галлюцинаций искусственного интеллекта: почему языковые модели все выдумывают и как их уменьшить
Галлюцинации ИИ объяснили: что это такое, почему языковые модели их создают, распространенные типы, как уменьшить их с помощью заземления и подсказо…
Читать статью →DevOpsОбъяснение переменных среды: файлы .env, секреты и правильно выполненная конфигурация
Переменные среды объясняются на примерах Python, которые мы запускали: их чтение и установка, почему каждое значение является строкой, файлы .env, н…
Читать статью →Разработка ПООбъяснение ограничения скорости API: алгоритмы, заголовки и обработка ошибок 429
Ограничение скорости API объясняется с помощью проведенного нами моделирования: сегмент токенов, фиксированное окно, скользящее окно, 429 ответов, з…
Читать статью →Автоматизация тестированияИздевательство в Python: unittest.mock, патч и ошибки, из-за которых тесты лгут
Издевательство на Python с помощью unittest.mock, протестировано: Mock и return_value, Side_effect, исправление использования имени, спецификация и …
Читать статью →Тестирование ПООбъяснение покрытия кода: покрытие линий или ветвей и что на самом деле означает 100%
Покрытие кода объяснено с помощью реального запуска покрытия.py: покрытие строк, ветвей и условий, чтение отчета, почему 100%-ное покрытие все еще м…
Читать статью →Базы данных и SQLDELETE, TRUNCATE и DROP в SQL: различия, откат и когда использовать каждый из них
Объяснение DELETE, TRUNCATE и DROP на примере протестированного SQL: что каждый из них удаляет, откат, триггеры, сброс идентификаторов, скорость и ч…
Читать статью →Базы данных и SQLОбъяснение представлений SQL: создавайте, используйте и избегайте ловушек, с примерами
Представления SQL, объясненные с помощью запросов, которые мы выполняли: создание представления, результаты в реальном времени, скрытие столбцов, пр…
Читать статью →Разработка ПОИнтерфейс TypeScript и тип: реальные различия, протестированные с помощью компилятора
Интерфейс TypeScript против типа, проверенный реальным компилятором: что могут делать оба, что может делать только один, слияние объявлений, расшире…
Читать статью →Разработка ПООбъяснение Python *args и **kwargs: гибкие функции с понятными примерами
Python *args и **kwargs, объясненные с помощью кода, который мы запускали: упаковка и распаковка, порядок параметров, аргументы только по ключевым с…
Читать статью →Разработка ПОЛямбда-функции Python: синтаксис, реальное использование и когда их не следует использовать
Лямбда-функции Python, объясненные с помощью кода, который мы запускали: синтаксис, сортировка с помощью ключа, карты и фильтра, замыкания, ловушка …
Читать статью →Искусственный интеллектЧто такое ИИ-агенты? Как они работают: можно запустить простой цикл агента
Что такое агенты ИИ? Простое руководство по циклу агента, инструментам, памяти и ограничениям, с небольшим циклом Python, который мы запустили, реал…
Читать статью →DevOpsМониторинг и наблюдаемость: реальная разница с журналами, метриками и трассировками
Объяснение мониторинга и наблюдаемости: известные и неизвестные сбои, журналы, метрики и трассировки, пошаговое руководство по инцидентам при оформл…
Читать статью →DevOpsDocker Volumes против Bind Mounts: правильное сохранение данных контейнера
Объяснение томов Docker и монтирования привязки: почему исчезают данные контейнера, именованные тома, монтирование привязки, tmpfs, примеры создания…
Читать статью →Автоматизация тестированияTDD против BDD: ключевые различия, одна функция, созданная для обоих способов, и когда использовать каждый
TDD и BDD объясняются с помощью одной функции скидки, созданной в обоих направлениях: красно-зеленый рефакторинг с кодом, который мы запускали, сцен…
Читать статью →Тестирование ПОПользовательское приемочное тестирование (UAT): процесс, контрольный список и реальные примеры
Приемочное тестирование пользователей объяснило: кто его проводит, чем оно отличается от контроля качества, семиэтапный процесс, шаблон тестового сц…
Читать статью →Базы данных и SQLОбъяснение значений NULL в SQL: IS NULL, COALESCE, NULLIF и ловушки, которых следует избегать
Значения SQL NULL объяснены с помощью запросов, которые мы выполнили: трехзначная логика, IS NULL, COALESCE, NULLIF, как агрегаты, соединения и сорт…
Читать статью →Базы данных и SQLОператор SQL CASE WHEN: синтаксис, реальные примеры и распространенные ошибки
SQL CASE WHEN объяснен с помощью запросов, которые мы выполняли: простые формы и формы для поиска, порядок условий, повороты с помощью SUM, трюки CO…
Читать статью →Разработка ПОЧто такое JWT? Веб-токены JSON, объясненные на рабочем примере
Что такое JWT? Объяснение JSON Web Tokens: три части: токен, созданный и измененный в коде, который мы запускали, JWT и сеансы, хранилище, токены об…
Читать статью →Разработка ПООбъяснение цикла событий JavaScript: стек вызовов, микрозадачи и таймеры с примерами
Цикл событий JavaScript объясняется с помощью кода, который мы запускали: стек вызовов, очереди задач и микрозадач, почему setTimeout 0 не является …
Читать статью →Разработка ПОГенераторы Python и объяснение доходности: экономьте память с помощью реальных примеров
Генераторы Python и доходность, объясненные с помощью кода, который мы запускали: как они приостанавливаются и возобновляются, измеряют экономию пам…
Читать статью →Искусственный интеллектЧто такое вложения? Простое объяснение векторов и косинусного сходства
Что такое вложения? Простое руководство по векторам, косинусному подобию и семантическому поиску с небольшим примером, который мы использовали, общи…
Читать статью →DevOpsСине-зеленое и канареечное развертывание: различия, компромиссы и как выбрать
Объяснение сине-зеленого и канареечного развертывания: как каждый из них работает, стоимость, риски и откат, стабильное группирование пользователей,…
Читать статью →Автоматизация тестированияНенадежные тесты: что их вызывает и как их исправить навсегда
Ненадежные тесты объясняются на демонстрациях, которые мы запускали: основные причины, способы подтверждения и диагностики, правильные исправления в…
Читать статью →Тестирование ПОНагрузочное тестирование и стресс-тестирование: различия, метрики и примеры k6
Объяснение нагрузочного и стресс-тестирования: что доказывает каждое из них, тесты на всплески и выдержки, важные метрики, почему средние значения в…
Читать статью →Тестирование ПОТестовый пример и тестовый сценарий: ключевые различия, примеры и когда использовать каждый
Объяснение тестового случая и тестового сценария: четкие определения, параллельная таблица, одна функция сброса пароля, взятая из сценариев в случаи…
Читать статью →Базы данных и SQLОптимизация SQL-запросов: чтение планов выполнения и исправление медленных запросов с указанием времени
Оптимизация SQL-запросов проверена на миллионе строк: читайте планы выполнения, добавляйте правильные индексы, исправляйте неразмещаемые фильтры, ис…
Читать статью →Базы данных и SQLSQL UNION против UNION ALL: различия, примеры и проверка производительности
SQL UNION и UNION ALL объяснены с помощью запросов, которые мы выполнили: дубликаты, правила, которым следуют оба, реальная ошибка данных, измеренна…
Читать статью →Разработка ПОУчебное пособие по API JavaScript Fetch: GET, POST, обработка ошибок и тайм-ауты
Учебное пособие по API JavaScript Fetch, протестированное на локальном сервере: GET и POST, почему не выдается ошибка 404, многоразовый помощник, ст…
Читать статью →Разработка ПООбработка файлов Python: чтение, запись, CSV, JSON и pathlib с примерами
Обработка файлов Python объясняется с помощью кода, который мы запускали: открытые режимы, чтение больших файлов, CSV, JSON, pathlib, двоичные файлы…
Читать статью →Разработка ПОКлассы и объекты Python: учебник ООП с реальными примерами
Классы и объекты Python, объясненные с помощью кода, который мы запускали: __init__, методы, атрибуты класса и экземпляра, наследование, свойства, м…
Читать статью →Искусственный интеллектЧто такое большая языковая модель? Как работают LLM, объяснено без шумихи
Что такое большая языковая модель? LLM объясняется простым языком: токены, встраивания, внимание, обучение, температурные и контекстные окна, крошеч…
Читать статью →DevOpsКоманды Linux для начинающих: основное руководство по терминалу с примерами
Команды Linux для начинающих, сгруппированные по задачам: навигация, файлы, просмотр журналов, grep, каналы, разрешения, процессы и архивы, с практи…
Читать статью →Автоматизация тестированияSelenium vs Playwright: ключевые различия, сравнение кода и что выбрать
Сравнение Selenium и Playwright: архитектура, ожидание, поддержка браузера и языка, отладка, одинаковый тест в обоих инструментах, советы по миграци…
Читать статью →Тестирование ПОРазделение эквивалентности: метод проектирования тестов, объясненный на рабочих примерах
Объяснение эквивалентного разделения: как найти действительные и недействительные разделы, рабочие примеры для возрастов, паролей и купонов, проведе…
Читать статью →Тестирование ПОСерьезность и приоритет в тестировании программного обеспечения: различия, матрица и реальные примеры
Объяснение серьезности и приоритета при тестировании программного обеспечения: четкие определения, четырехсторонняя матрица с примерами, шкалы уровн…
Читать статью →Базы данных и SQLУчебное пособие по подзапросам SQL: типы, коррелирующие подзапросы и EXISTS с примерами
Руководство по подзапросам SQL с выполненными нами запросами: скалярные подзапросы, IN, FROM и SELECT, коррелированные подзапросы, EXISTS, ловушка N…
Читать статью →Базы данных и SQLОбъяснение транзакций SQL и ACID: COMMIT, ROLLBACK и уровни изоляции
Транзакции SQL и ACID, объясненные с помощью банковского перевода, который мы выполнили: COMMIT, ROLLBACK, точки сохранения, блокировки, уровни изол…
Читать статью →Разработка ПОGit merge vs rebase: отличия, примеры и когда что использовать
Git merge vs rebase с реальным выводом log, сравнительной таблицей, золотым правилом rebase и безопасным рабочим процессом для команды.
Читать статью →Разработка ПОГенераторы списков в Python: синтаксис, примеры и когда их избегать
Генераторы списков в Python: синтаксис, фильтры, if-else, вложенные циклы, измеренное время и случаи, когда обычный цикл for лучше.
Читать статью →Базы данных и SQLSQL JOIN: объяснение с примерами (Inner, Left, Right, Full)
Разберите SQL JOIN шаг за шагом на примерах таблиц и запросов: INNER, LEFT, RIGHT, FULL, CROSS и SELF, а также частые ошибки и советы по производите…
Читать статью →