Что доказывает расшифрованный JWT
Декодер JWT показывает вам, что утверждает токен. Он не может показать вам, верны ли эти утверждения. В этом руководстве рассказывается, что представляют собой три сегмента, что декодирование устанавливает, а что нет, а также атаки, которые существуют в промежутке между ними.
Три сегмента, два из которых всего лишь JSON
JWT в своей общей форме — это JWS: три сегмента base64url, разделенные точками. Первый — это заголовок, второй — полезная нагрузка, третий — подпись.
Заголовок и полезные данные представляют собой обычные объекты JSON, закодированные в формате Base64url. Закодировано, а не зашифровано. Любой, у кого есть жетон, может прочитать оба, мгновенно, без ключа — это не недостаток, это дизайн. JWT — это подписанное заявление, а не запечатанный конверт. Подпись гарантирует, что заявление не было изменено; он не делает ничего, чтобы сохранить конфиденциальность.
Последствие стоит сказать прямо, потому что оно обычно упускается из виду: никогда не помещайте ничего конфиденциального в полезную нагрузку JWT. Ни пароля, ни полного национального идентификатора, ни внутренних реквизитов системы. Предположим, что полезные данные являются общедоступными, потому что они доступны всем, у кого есть токен.
Третий сегмент — это подпись, рассчитанная по первым двум. Это единственная часть, которая несет какое-либо значение безопасности, и ее декодер не может оценить.
Что доказывает расшифровка: ничего
В этом суть всего руководства. Декодирование JWT анализирует две строки base64url в JSON. Это подтверждает, что токен правильно сформирован. Он не подтверждает, что токен подлинный, что он был выдан стороной, указанной в утверждении «iss», что утверждения не редактировались или что он когда-либо был действительным.
Любой может создать токен. Возьмите любой JWT, измените «role»: «user» на «role»: «admin», перекодируйте полезную нагрузку, прикрепите любую подпись в конце, и декодер отобразит ваши отредактированные утверждения точно так же уверенно, как и оригинал. У него нет возможности узнать разницу, поскольку проверка разницы — это другая операция, требующая ключа, которого нет у декодера.
Поэтому, когда декодер — этот или любой другой — показывает вам «exp: 2026-01-01», он на самом деле говорит вам следующее: этот токен содержит утверждение о том, что срок его действия истекает в эту дату. Будет ли это утверждение иметь какое-либо значение, полностью зависит от того, действительна ли подпись, которая не была проверена.
Этот инструмент только декодирует и каждый раз сообщает об этом на странице рядом с результатами. Не в сноске. Причина в том, что декодер, который хранит молчание об этом, учит своих пользователей читать непроверенные данные, как если бы они были проверены, и эта привычка является корнем целого семейства ошибок аутентификации.
Почему этот инструмент не предлагает проверку
Для проверки необходимы три вещи, которые веб-страница не может иметь ответственно: ключ эмитента, заранее закрепленный алгоритм и политика относительно того, что отклонять.
Ключом является очевидная проблема. Для алгоритмов HMAC (HS256 и его друзей) ключ представляет собой общий секрет — тот же секрет, который используется для создания токенов. Вставка его на веб-страницу означает вставку учетных данных, которые могут создавать действительные токены, на веб-страницу. Для RSA и ECDSA открытый ключ не является секретным, но вам все равно нужно будет получить правильный ключ из правильной конечной точки JWKS и доверять ему.
Алгоритм — это тонкая проблема и источник двух известных атак. Первый — alg: «none»: заголовок утверждает, что токен не подписан, и верификатор, который учитывает заголовок, а не свою собственную конфигурацию, принимает что угодно. Второй — путаница RS256 с HS256: злоумышленник берет открытый ключ, который по определению является общедоступным, меняет заголовок на HS256 и подписывает токен, используя этот открытый ключ, как секрет HMAC. Верификатор, который считывает алгоритм из токена и ищет «ключ», проверит его.
Обе атаки происходят из одной и той же ошибки: токен сообщает верификатору, как его проверять. Правильный верификатор игнорирует алгоритм заголовка и использует тот, который был настроен. Это решение принадлежит системе, которая доверяет токен, а не удобному инструменту и не тому, кто вставил что-то в форму.
Почему бы не вставить куда угодно токены производства
Маркер доступа — это учетные данные на предъявителя. Именно это означает «носитель» в заголовке авторизации: тот, кто его носит, — это вы. Второго фактора нет, и обычно невозможно отличить украденный токен от законного. До истечения срока его действия это рабочий ключ к вашей учетной записи.
Таким образом, вставка живого токена на любую веб-страницу означает передачу учетных данных этой странице. Этот декодирует все локально и не делает сетевых запросов после загрузки страницы — вы можете подтвердить это на сетевой панели вашего браузера, и вам следует это сделать, потому что это занимает десять секунд. Но обратите внимание, что на самом деле представляет собой этот аргумент: заявление на веб-сайте о том, что веб-сайт заслуживает доверия. Каждый сайт, который занимается кражей токенов, заявляет одно и то же, и посетитель не может сразу заметить разницу.
Безопасная привычка не зависит от правильной оценки сайтов. Используйте токены с истекшим сроком действия, токены тестовой среды или токены, которые вы создали для этой цели. Если вы уже вставили производственный токен куда-нибудь — поверните его. Отзыв стоит дешево; инцидента нет.
То же самое, но в большей степени, относится и к подписанию ключей. Нет законной причины вводить секрет HMAC или закрытый ключ на веб-страницу, и любой сайт, запрашивающий «проверку» вашего токена, запрашивает возможность подделывать токены. Это конкретная причина, по которой этот инструмент не имеет функции проверки: эта функция требует запроса.
Чтение утверждений, которые имеют значение
RFC 7519 регистрирует небольшой набор имен утверждений. «iss» — это эмитент, «sub» — тема, о которой рассказывает токен, «audi» — целевая аудитория, «exp» — срок действия, «nbf» — самое раннее время действия, «iat» — время выпуска и «jti» — уникальный идентификатор для обнаружения повтора. Все остальное зависит от приложения.
Утверждения времени представляют собой значения NumericDate: секунды с начала эпохи Unix, а не миллисекунды. Это постоянно сбивает людей с толку, поскольку большинство значений времени JavaScript составляют миллисекунды. Токену, срок действия которого истекает через 1970, обычно присваивается значение в миллисекундах; тот, срок действия которого истекает в году 55000, обычно где-то имеет второе значение, умноженное на 1000.
«audi» заслуживает особого внимания при отладке. Абсолютно действительный токен может оказаться неправильным, поскольку он был выпущен для другой аудитории. Верификатор, который проверяет подпись, но не аудиторию, полностью примет токен, созданный для другой службы, что является реальным путем повышения привилегий в системах, которые используют одного и того же поставщика удостоверений.
Этот инструмент отображает утверждения времени в UTC, помечает токен с истекшим сроком действия как истекший и объединяет это с напоминанием о том, что утверждение об истечении срока действия что-то значит только в том случае, если подпись действительна. Напоминание существует, потому что «он говорит, что срок его действия еще не истек» — это именно тот момент, когда привычка к непроверенным данным наносит свой вред.
Краткий контрольный список для системы, выполняющей доверительные функции
Если вы пишете код, который принимает токены, а не просто проверяет их, ниже приведена краткая версия того, что делает правильный верификатор.
- Прежде чем читать какие-либо претензии, сначала проверьте подпись с помощью ключа, полученного вами вне компании.
- Закрепите алгоритм в своей конфигурации. Никогда не читайте его из заголовка токена. Отклонить «нет» безоговорочно.
- Проверьте «exp» и «nbf» по проверенным часам с минимальным допуском на асимметрию.
- Проверьте «iss» и «audi» на соответствие ожидаемым значениям. Действительная подпись на токене, предназначенном для кого-то другого, по-прежнему является неправильным токеном.
- Используйте проверенную библиотеку для своей платформы, а не собирайте ее самостоятельно. Каждый элемент в этом списке присутствует в нем, потому что реализации ошиблись.
- Сохраняйте короткий срок действия токена и предусмотрите путь отзыва. Короткоживущие токены ограничивают ущерб от утечки, которую вы еще не заметили.
Что происходит с тем, что вы вставляете
- Каждое преобразование, хеширование, декодирование и различие выполняются на вкладке вашего браузера. Никакие входные данные не загружаются, не регистрируются и не сохраняются на сервере, поскольку после загрузки страницы сервер не задействован.
- Хэши берутся из собственной реализации Web Crypto в браузере, а UUID — из криптографически безопасного генератора случайных чисел. Ни один из них не предполагает сетевой вызов.
- Ничего из того, что вы вводите, не записывается в локальное хранилище или файл cookie. Перезагрузка страницы удаляет ее; закрытие вкладки отменяет ее.
- Аналитика в масштабе всего сайта выполняется только на настроенном каноническом рабочем хосте и описана в Политике конфиденциальности; локальные и предварительные хосты отказываются от этого. Вставленные значения, токены, URL-адреса и содержимое файлов исключаются из собственных аналитических событий ToolAcre. Реклама отключена в текущей конфигурации.
- Тем не менее: ключ JWT или API — это действующие учетные данные. Безопасная привычка — никогда не вставлять его на веб-страницу, которую вы не писали, какими бы заслуживающими доверия ни были его утверждения, включая эту.
Вопросы
Этот инструмент проверяет подпись?
Нет, и никогда не будет. Он декодирует заголовок и полезную нагрузку и показывает, что они содержат. Он не проверяет подпись, поэтому ничто, что он отображает, не доказывает, что токен является подлинным, неизмененным или выдан тем, кого он называет.
Тогда как я узнаю, что токен подлинный?
Путем проверки подписи с помощью ключа эмитента с использованием проверенной библиотеки и алгоритма, закрепленного в вашей собственной конфигурации, а не чтения с токена. Это работа службы, которая доверяет токену, в среде, которая законно владеет ключом.
Отправляется ли мой токен куда-нибудь, когда я его декодирую здесь?
Нет. Декодирование происходит на вкладке вашего браузера с использованием собственного JavaScript страницы, и после загрузки страница не отправляет сетевые запросы. Вы можете убедиться в этом на сетевой панели вашего браузера. Вам все равно не следует вставлять производственные токены в веб-инструменты по привычке, потому что эта привычка должна работать на сайтах, которые не честны в этом отношении.
Почему кто-либо может прочитать мою полезную нагрузку JWT?
Потому что полезная нагрузка закодирована в формате Base64url, а не зашифрована. JWS — это подписанный оператор, а не запечатанный. Если вам нужно, чтобы содержимое было нечитаемым, вам нужен JWE, формат зашифрованного токена — и тогда декодер вообще ничего не сможет вам показать без ключа.
Что такое alg: «нет»?
Значение заголовка, объявляющее, что токен является беззнаковым. Он существует в спецификации для контекстов, где целостность гарантируется другими средствами, и это постоянная ловушка: верификатор, доверяющий алгоритму заголовка, примет любой токен, утверждающий «нет». Этот инструмент помечает его всякий раз, когда он появляется.
Мой токен имеет пять сегментов и не будет декодироваться. Почему?
Пять сегментов означают JWE — зашифрованный токен, а не подписанный JWS. Его содержимое невозможно прочитать без ключа дешифрования, поэтому декодеру действительно нечего показывать. Этот инструмент явно идентифицирует этот случай, а не сообщает о неопределенном сбое анализа.
Срок действия выглядит неправильно в 1000.
JWT утверждает время NumericDate: секунды с начала эпохи, а не миллисекунды. Значение, полученное с помощью Date.now(), в тысячу раз больше. Утилита timestamp в этом наборе инструментов преобразует эти два значения и всегда сообщает вам, какую единицу измерения она использовала.
Безопасно ли хранить JWT в localStorage?
Это компромисс, а не «да» или «нет». localStorage доступен для чтения любому JavaScript, работающему в вашем источнике, поэтому одна уязвимость XSS приводит к краже токена. Файл cookie httpOnly не читается JavaScript, но требует защиты CSRF. Честно говоря, ни один из них не является бесплатным, и решение зависит от модели угроз вашего приложения.
Ограничения
- Этот инструмент только декодирует. Он не проверяет подписи, и это постоянное дизайнерское решение, а не отсутствующая функция — почему см. руководство выше.
- Зашифрованные токены (JWE, пять сегментов) вообще невозможно декодировать без ключа. Инструмент идентифицирует их и останавливается.
- Вложенные JWT — токены, полезная нагрузка которых сама является токеном — не разворачиваются автоматически. Раскодируйте внутренний токен как отдельный шаг.
- Значения утверждений, выходящие за пределы зарегистрированного набора, определенного в RFC 7519, зависят от приложения, поэтому инструмент показывает их значения, не интерпретируя их.
- Показанный здесь срок действия отражает только то, что токен заявляет о себе. Имеет ли это утверждение смысл, зависит от подписи, которую этот инструмент не проверяет.
- Токены размером более 200,000 символов отклоняются. Любой реальный JWT на порядки меньше.