Что такое веб-токен JSON (JWT)?
23 апреля 2022 г.JWT или веб-токены JSON чаще всего используются для идентификации аутентифицированного пользователя. Они выдаются сервером аутентификации и используются клиент-сервером (для защиты его API).
Ищете разбивку по веб-токенам JSON (JWT)? Вы находитесь в правильном месте. Мы рассмотрим:
- Что такое JWT?
- Структура JWT
- Соглашения о требованиях JWT
- Как они работают (на примере)?
- Плюсы и минусы JWT
- Общие проблемы во время разработки
- Дополнительные материалы для чтения
Что такое JWT?
JSON Web Token — это открытый отраслевой стандарт, используемый для обмена информацией между двумя объектами, обычно клиентом (например, внешним интерфейсом вашего приложения) и сервером (бэкендом вашего приложения).
Они содержат объекты JSON, содержащие информацию, которой необходимо поделиться. Каждый JWT также подписывается с использованием криптографии (хеширования), чтобы гарантировать, что содержимое JSON (также известное как утверждения JWT) не может быть изменено клиентом или злоумышленником.
Например, когда вы входите в систему с помощью Google, Google выдает JWT, который содержит следующие утверждения/полезную нагрузку JSON:
```json
"исс": "https://accounts.google.com",
"azp": "1234987819200.apps.googleusercontent.com",
"ауди": "1234987819200.apps.googleusercontent.com",
"sub": "10769150350006150715113082367",
"at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q",
"электронная почта": "jsmith@example.com",
"email_verified": "правда",
"иат": 1353601026,
"эксп": 1353604926,
"нонс": "0394852-3190485-2490358",
"hd": "example.com"
Используя приведенную выше информацию, клиентское приложение, использующее вход в Google, точно знает, кто является конечным пользователем.
Что такое токены и зачем они нужны?
Вам может быть интересно, почему сервер аутентификации не может просто отправить информацию в виде простого объекта JSON и почему ему нужно преобразовать ее в «токен».
Если сервер авторизации отправляет его в виде простого JSON, у API-интерфейсов клиентского приложения не будет возможности проверить правильность получаемого контента. Злоумышленник может, например, изменить идентификатор пользователя (заявка sub
в приведенном выше примере JSON), и API-интерфейсы приложения не смогут узнать об этом.
Из-за этой проблемы безопасности сервер аутентификации должен передавать эту информацию таким образом, чтобы клиентское приложение могло ее проверить, и именно здесь появляется понятие «токен».
Проще говоря, токен — это строка, содержащая некоторую информацию, которую можно безопасно проверить. Это может быть случайный набор буквенно-цифровых символов, указывающих на идентификатор в базе данных, или это может быть закодированный JSON, который может быть проверен клиентом (известный как JWT).
Структура JWT
JWT состоит из трех частей:
- Заголовок: Состоит из двух частей:
- Используемый алгоритм подписи.
- Тип токена, в данном случае чаще всего «JWT».
- Полезная нагрузка: полезная нагрузка содержит утверждения или объект JSON.
- Подпись: строка, созданная с помощью криптографического алгоритма, который можно использовать для проверки целостности полезной нагрузки JSON.
Позже в этом посте мы создадим собственный JWT с нуля!
Соглашение об утверждении JWT
Вы могли заметить, что в приведенном выше примере JWT (выпущенном Google) полезная нагрузка JSON имеет неочевидные имена полей. Они используют sub
, iat
, aud
и так далее:
- iss: эмитент токена (в данном случае Google)
- azp и aud: идентификаторы клиентов, выданные Google для вашего приложения. Таким образом, Google знает, какой веб-сайт пытается использовать его службу входа, а веб-сайт знает, что JWT был выпущен специально для них.
- sub: идентификатор пользователя Google конечного пользователя.
- at_hash: Хэш токена доступа. Токен доступа OAuth отличается от JWT тем, что это непрозрачный токен. Маркер доступа предназначен для того, чтобы клиентское приложение могло запрашивать у Google дополнительную информацию о вошедшем в систему пользователе.
- email: идентификатор электронной почты конечного пользователя.
- email_verified: подтвердил ли пользователь свою электронную почту.
- iat: время (в миллисекундах с начала эпохи) создания JWT.
- exp: время (в миллисекундах с начала эпохи) создания JWT.
- nonce: может использоваться клиентским приложением для предотвращения повторных атак.
- hd: размещенный домен пользователя G Suite.
Причина использования этих специальных ключей заключается в том, чтобы следовать отраслевому соглашению об именах важных полей в JWT. Следование этому соглашению позволяет клиентским библиотекам на разных языках проверять действительность JWT, выдаваемых любыми серверами аутентификации. Например, если клиентской библиотеке необходимо проверить, истек ли срок действия JWT, она будет просто искать поле iat
.
Как они работают (на примере)
Самый простой способ объяснить, как работает JWT, — это пример. Мы начнем с создания JWT для конкретной полезной нагрузки JSON, а затем перейдем к ее проверке:
1) Создайте JSON
Возьмем следующую минимальную полезную нагрузку JSON:
```json
"идентификатор пользователя": "abcd123",
"срок действия": 1646635611301
2) Создайте ключ подписи JWT и определите алгоритм подписи
Во-первых, нам нужен ключ подписи и алгоритм для использования. Мы можем сгенерировать ключ подписи, используя любой безопасный случайный источник. Для целей этого поста давайте использовать:
- Подписание ключа:
NTNv7j0TuYARvmNMmWXo6fKvM4o6nv / aUi9ryX38ZH + L1bkrnD1ObOQ8JAUmHCBq7Iy7otZcyAagBLHVKvvYaIpmMuxmARQ97jUVG16Jkpkp1wXOPsrF9zwew6TpczyHkHgX5EuLg2MeBuiT / qJACs1J0apruOOJCg / gOtkjB4c =
- Алгоритм подписи: HMAC + SHA256, также известный как HS256.
3) Создание "Заголовка"
Он содержит информацию о том, какой алгоритм подписи используется. Как и полезная нагрузка, это также JSON и будет добавлено к началу JWT (отсюда и название заголовка):
```json
"тип": "JWT",
"алг": "HS256"
4) Создать подпись
- Во-первых, мы удаляем все пробелы из полезной нагрузки JSON, а затем кодируем ее в base64, чтобы получить
eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ
. Вы можете попробовать вставить эту строку в [онлайн-декодер base64] (https://www.base64decode.org/), чтобы получить наш JSON.
- Точно так же мы удаляем пробелы из заголовка JSON и кодируем его в base64, чтобы получить:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.
- Мы объединяем обе строки с основанием 64 с символом
.
в середине, например<header>.<payload>
, что дает нам eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfMzAxf. Нет особой причины делать это таким образом, кроме как установить соглашение, которому может следовать отрасль.
- Теперь мы запускаем функцию
Base64 + HMACSHA256
для приведенной выше объединенной строки и секрета, чтобы получить подпись:
``текст
Base64URLSafe(
HMACSHA256 ( "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ", "NTNv7j0TuYARvmNMmWXo6fKvM4o6nv / aUi9ryX38ZH + L1bkrnD1ObOQ8JAUmHCBq7Iy7otZcyAagBLHVKvvYaIpmMuxmARQ97jUVG16Jkpkp1wXOPsrF9zwew6TpczyHkHgX5EuLg2MeBuiT / qJACs1J0apruOOJCg / gOtkjB4c =")
Результат:
3Thp81rDFrKXr3WrY1MyMnNK8kKoZBX9lg-JwFznR-M
Мы кодируем его в base64 только как отраслевое соглашение.
5) Создание JWT
Наконец, мы добавляем сгенерированный секрет, например <header>.<body>.<secret>
, чтобы создать наш JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ.3Thp81rDFrKXr3WrY1MyMnNK8kKoZBX9lg-JwFznR-M
6) Проверка JWT
Как только клиент отправляет JWT обратно на сервер, сервер выполняет следующие шаги:
- Извлекает часть заголовка JWT (
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
).
- Декодирует ли он base64, чтобы получить простой текст JSON:
{"typ":"JWT","alg":"HS256"}
- Проверяет, что значение поля
typ
равноJWT
, аalg
равноHS256
. В противном случае он отклонил бы JWT.
- Извлекает секретный ключ подписи и выполняет ту же операцию
Base64URLSafe(HMACSHA256(...))
, что и шаг номер 4, для заголовка и тела входящего JWT. Обратите внимание, что если тело входящего JWT отличается, на этом шаге будет сгенерирована другая подпись, чем на шаге (4).
- Проверяет, совпадает ли сгенерированная подпись с подписью из входящего JWT. Если это не так, то JWT отклоняется.
- Мы декодируем тело JWT с помощью base64 (
eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ
), чтобы получить{"userId":"abcd123","expiry":1646635611301}
.
- Мы отклоняем JWT, если текущее время (в миллисекундах) больше, чем время «истечения» JSON (поскольку срок действия JWT истек).
Мы можем доверять входящему JWT, только если он проходит все указанные выше проверки.
Плюсы и минусы JWT
Использование JWT имеет ряд преимуществ:
- Безопасность: JWT имеют цифровую подпись с использованием либо секретного (HMAC), либо пары открытого/закрытого ключа (RSA или ECDSA), что защищает их от изменения клиентом или злоумышленником.
- Хранится только на клиенте: вы создаете JWT на сервере и отправляете их клиенту. Затем клиент отправляет JWT с каждым запросом. Это экономит место в базе данных.
- Эффективность/без сохранения состояния: JWT можно быстро проверить, поскольку он не требует поиска в базе данных. Это особенно полезно в больших распределенных системах.
Тем не менее, некоторые из недостатков:
- Безотзывный: из-за их автономного характера и процесса проверки без сохранения состояния может быть сложно отозвать JWT до истечения срока его действия естественным образом. Поэтому такие действия, как немедленная блокировка пользователя, не могут быть легко реализованы. При этом есть способ поддерживать [запретить/черный список JWT] (https://supertokens.com/blog/revoking-access-with-a-jwt-blacklist), и благодаря этому мы можем немедленно отозвать их. .
- Зависит от одного секретного ключа: Создание JWT зависит от одного секретного ключа. Если этот ключ скомпрометирован, злоумышленник может создать свой собственный JWT, который примет уровень API. Это, в свою очередь, означает, что если секретный ключ скомпрометирован, злоумышленник может подделать личность любого пользователя. Мы можем уменьшить этот риск, время от времени меняя секретный ключ.
Подводя итог, JWT наиболее полезен для крупномасштабных приложений, которые не требуют таких действий, как немедленная блокировка пользователя.
Распространенные проблемы во время разработки
JWT отклонен
Эта ошибка означает, что процесс проверки JWT завершился неудачно. Это могло произойти, потому что:
- Срок действия JWT уже истек
- Подпись не совпала — это означает, что либо изменились ключи подписи, либо тело JSON было изменено.
※ Другие претензии не проверить. Например, в приведенном выше примере Google JWT, если JWT был сгенерирован для App1, но отправлен в App2, App2 отклонит его (поскольку утверждение aud
будет указывать на идентификатор App1).
Токен JWT не поддерживает требуемую область действия
Утверждения в JWT могут представлять области или разрешения, предоставленные пользователем. Например, конечный пользователь может только согласиться с тем, что приложение может только читать его данные, но не изменять их. Однако приложение может ожидать, что пользователь также согласится изменить данные. В этом случае область, требуемая приложением, не соответствует JWT.
Декодирование JWT не удалось
Эта ошибка может возникнуть, если JWT имеет неверный формат. Например, клиент может ожидать, что JWT закодирован в base64, но сервер аутентификации не закодировал его в bas63.
Дальнейшее понимание JWT
В SuperTokens мы предоставляем решение для аутентификации с открытым исходным кодом, целью которого является абстрагирование от всех сложностей использования JWT. Мы позаботимся об их создании, проверке и обновлении. Кроме того, мы автоматически устраняем некоторые минусы, упомянутые выше.
Оригинал