Перейти к основному содержимому

Важен ли порядок полей в JWT

· 2 мин. чтения

Всем известна проблема, что JSON по умолчанию не гарантирует порядок полей. Но ведь в JWT порядок полей важен, ведь так? Нам же нужно подписать полученные строки, а они каждый раз могут быть разные. Давайте разберемся.

Из чего состоит JWT

JWT, а именно его вариант JWS, состоит из трех частей: заголовка, полезной нагрузки и подписи.

Заголовок

{
"alg": "HS256",
"typ": "JWT"
}

Заголовок содержит алгоритм подписи и тип токена. В нашем случае это HS256 и JWT соответственно. Подробнее про алгоритмы подписи можно почитать тут.

Полезная нагрузка

{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}

Полезная нагрузка содержит набор claims, которые описывают токен. В нашем случае это sub, name и iat. Подробнее про claims можно почитать тут.

Как мы видим в заголовке 2 поля, которые могут быть сериализованы в строку как минимум 2 способами:

{"alg":"HS256","typ":"JWT"}

или

{"typ":"JWT","alg":"HS256"}

Так же и с полезной нагрузкой. Количество вариантов описывается формулой n!, где n - количество полей. В нашем случае это 3! = 6 вариантов. Домножим на количество вариантов заголовка и получим 6 * 2 = 12 вариантов.

Неужели из этих 12 вариантов только один будет правильным? Давайте проверим.

Проверка

Генерировать токены будем с помощью jwt.io используя стандартный секрет your-256-bit-secret.

Возьмем первый заголовок и наш пейлоад.

{"alg":"HS256","typ":"JWT"}

Получим следующий токен.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.c9MgNpg3JJH0yZn_jRWBo9l9FD3LDk_Y7k_lvIieXl0

А теперь второй вариант заголовка.

{"typ":"JWT","alg":"HS256"}

Для него токен будет выглядеть так.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.H0ObFx96gjmsRNJ1ipubz2PORU_SDfYFQRnwQt1BAuo

Что же мы видим? Заголовок конечно поменялся. Это легко заметить записав заголовки рядом.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

Также поменялась подпись.

c9MgNpg3JJH0yZn_jRWBo9l9FD3LDk_Y7k_lvIieXl0
H0ObFx96gjmsRNJ1ipubz2PORU_SDfYFQRnwQt1BAuo

Помешало ли это нам сформировать корректный токен? Нет. Мы все еще можем сформировать токен с правильной подписью. Если внимательно изучить RFC, то можно заметить, что заголовок и payload даже не должны быть отформатированы каким-то особенным образом. Единственное требование, которое предъявляется к ним (п. 10), это то, что это должны быть валидные JSON объекты.

Перемешивая порядок полей и для payload, можно легко убедиться, что для любого порядка полей можно сформировать корректный токен.

Так как для проверки подписи по RFC не требуется десериализация и последующая сериализация, то порядок полей в объектах не важен.