Общие сведения о подписании RSA для JWT

Я внедряю систему входа с помощью схемы JWT (JSON Web Token). Как правило, после того, как пользователь входит в систему/входит в систему, сервер подписывает JWT и передает его клиенту.

Затем клиент возвращает токен с каждым запросом, и сервер проверяет токен перед отправкой ответа.

Это в значительной степени, как вы ожидаете, но у меня есть некоторые проблемы с логикой процесса. Из всех математических статей, которые я прочитал, кажется, что для подписи RSA используются асимметричные ключи. Поскольку открытый ключ, как следует из его названия, предоставляется клиенту, а закрытый ключ хранится на сервере, имеет смысл подписать JWT открытым ключом, который отправляется клиенту, и проверить его на стороне сервера, используя закрытый ключ.

Однако на каждом примере и библиотеке я вижу, что все наоборот. Есть идеи, почему это так? Если JWT подписан с закрытым ключом и проверен с открытым ключом, в чем смысл?

Ответ 1

Прежде всего, извинения, этот ответ получился довольно длинным.

Если вы используете RSA для подписи своих токенов, а подключающийся клиент - это веб-браузер, клиент никогда не увидит ключи RSA (открытые или закрытые). Это связано с тем, что клиенту, по-видимому, не нужно проверять допустимость JWT, это должен делать только сервер. Клиент просто держит JWT и показывает его серверу при запросе. Затем сервер проверяет его действительность при просмотре токена.

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

Вы можете подписать JWT несколькими различными алгоритмами, RSA является одним из них. Другими популярными вариантами подписи вашего JWT являются алгоритмы ECDSA или HMAC (стандарт JWT также поддерживает другие). HMAC, в частности, не является схемой открытого/закрытого ключа. Там только один ключ, ключ, который используется для подписи и проверки токенов. Вы можете думать об этом как об использовании закрытого ключа для подписи и проверки JWT. Я ни в коем случае не эксперт в этом вопросе, но вот выводы, к которым я пришел после недавнего собственного исследования:

Использование HMAC приятно, потому что это самый быстрый вариант. Однако, чтобы проверить JWT, вам нужно дать кому-то один ключ, который делает все. Совместное использование этого ключа с кем-то еще означает, что этот человек теперь может также подписывать токены и притворяться, будто они вы. Если вы создаете несколько серверных приложений, которые все должны иметь возможность проверять ваши JWT, вы, возможно, не захотите, чтобы каждое приложение также имело возможность подписывать токены (разные программисты могут поддерживать разные приложения, разделяя возможность подписи с более люди - это угроза безопасности и т.д.). В этом случае лучше иметь один строго контролируемый закрытый ключ (и одно приложение, которое выполняет подпись), а затем делиться открытым ключом с другими людьми, чтобы дать им возможность проверять токены. Здесь закрытый ключ используется для подписи токенов, а открытый ключ - для их проверки. В этом случае вы захотите выбрать RSA или ECDSA.

Например, у вас может быть экосистема приложений, которые все подключаются к та же база данных. Чтобы войти в систему, каждое приложение отправляет людей одному, выделенное приложение "вход в систему". Это приложение имеет закрытый ключ. Другой приложения могут проверить, что человек вошел в систему с помощью открытого ключа (но они не могут войти в систему).

Проведенное мной исследование указывает на то, что RSA является лучшим вариантом для большинства приложений JWT в этом сценарии. Это потому, что теоретически ваше приложение будет часто проверять токены. RSA намного быстрее, чем ECDSA при проверке. ECDSA в первую очередь хорош, потому что клавиши меньше по размеру. Это делает его лучше для сертификатов HTTPS, потому что вам нужно отправить открытый ключ в браузер клиента. Однако в сценарии JWT ключи остаются на сервере, поэтому размер хранилища не указан, а скорость проверки важнее.

Вывод: если вы создаете маленькое приложение без нескольких небольших "приложений микро-услуг"/вы единственный разработчик, возможно, выберите HMAC для шифрования ваших ключей. В противном случае, вероятно, выберите RSA. Опять же, я не эксперт, просто кто-то, кто недавно гуглил эту тему, так что возьмите это с крошкой соли.

Ответ 2

Ваше предложение:

имеет смысл подписать JWT с открытым ключом, который отправляется на клиента и проверить его на стороне сервера с помощью закрытого ключа.

неверно. Подписание выполняется с помощью закрытого ключа сервера, шифрование выполняется с помощью открытого ключа клиента. Вот как работает PKI в целом.

Ответ 3

Существует различие между подписанием/проверкой и шифрованием/дешифрованием данных, но семантика может быть схожей.

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

Вы шифруете данные с помощью открытого ключа и расшифровываете с помощью закрытого ключа. Это звучит противоположно, но на самом деле следует той же логической концепции, что и подписание. Если вы хотите отправить данные между человеком A и лицом B, у обоих людей есть пара открытых/закрытых ключей, и они делятся своими открытыми ключами друг с другом, когда встречаются (рукопожатие). A создает сообщение для B, шифрует его с использованием открытого ключа B и отправляет его B. Теперь никто без закрытого ключа B не может расшифровать это сообщение, включая A, даже если они изначально отправили его.

С точки зрения JWT, полезная нагрузка JWT сама по себе является просто JSON в кодировке Base64 с некоторыми стандартизированными полями. Подпись позволяет человеку с открытым ключом проверять, что информация не была изменена кем-то посередине. Похоже на контрольную сумму, но с некоторыми дополнительными защитными теплыми нечеткими чувствами. Контент подписанного JWT легко виден (base64 кодирует как Unicode или UTF-8, а не шифрует) конечному пользователю и любому посреднику, поэтому обычно не одобряют отправку конфиденциальных данных в паролях, подобных JWT, или PII.

Как уже упоминалось, большинство JWT содержат информацию, предназначенную не для клиентов, а для облегчения части служб RESTful, не имеющих состояния. Как правило, JWT будет содержать учетные записи, идентификаторы пользователей и часто разрешения в качестве "утверждений". Конечная точка API может проверять подпись и разумно доверять утверждениям, которые не будут изменены клиентом. Когда клиент отправляет JWT для каждого запроса, конечная точка вынуждена выполнять множество операций с базой данных взад и вперед, просто чтобы найти свое место, просто проверив подпись с открытым ключом.

Кроме того, подписанные JWT могут быть зашифрованы. Согласно спецификации JWE, полезная нагрузка шифруется после подписания, а затем дешифруется перед проверкой. Компромисс здесь заключается в том, что все конечные точки также должны иметь закрытый ключ для расшифровки JWT, но конечные пользователи не смогут увидеть содержимое JWT. Я говорю об обмене, потому что в целом закрытые ключи предназначены для обеспечения безопасности, а широко распространенный закрытый ключ просто менее безопасен. Безопасность, оценка рисков и стоимость/польза от шифрования - это совсем другой зверь :)