Важен ли порядок полей в JWT
Всем известна проблема, что 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 не требуется десериализация и последующая сериализация, то порядок полей в объектах не важен.