Как стать автором
Обновить

Комментарии 60

вы не правильно пишите что application server должен знать секрет, какой же это секрет если о нем знает несколько сервисов
Смысл в том что application server может читать данные из payload без ключа, а когда надо их проверить (verify) то он может взять этот же токен и отправить его на сервер авторизации, а он уже зная секрет сообщит валидный это токен или нет.

Нет, смысл вовсе не в этом. Строго говоря «application server может читать данные из payload без ключа» — это очень плохая идея. Никакого доверия таким данным нет и их можно использовать только после того, как сделана проверка подписи. Это действительно делает application server и он делает это на каждый запрос в котором JWT представлен.

Какой-то ключ application server должен знать, но это не обязательно должен быть тот же секрет, который использовался при создании токена. А может быть и тот-же, зависит от Signing Method — HMAC (shared key) или RSA/ECDSA (asymmetric).

давай те по порядку.
В статье говориться что секрет это то с помощью чего подписывается, далее в статье упоминается что секрет знают и сервер авторизации и сервер приложений, я лишь подчеркнул что это не правильно
Если подпись идет с помощью rsa то там ПУБЛИЧНЫЙ (не секретный) ключ и его может знать вообще кто угодно, а приватный ключ у сервера авторизации останется, как описали в коменте ниже.
При этом больше замечаний к статье не имею и в целом она очень адекватно написана, я лишь указал на то что неправильно трактовать можно прочитав статью

это вполне вполне правильная трактовка. HMAC именно так (общий секрет) и работает.

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


в примере в статье речь о симметричном шифрование и секрет получается один и тот же что на auth сервисе, что на application.

> в примере в статье речь о симметричном шифрование и секрет получается один и тот же что на auth сервисе, что на application.

да, именно об этом и идет речь в статье. И это, повторю в 3й раз, вполне правильная трактовка. С точки зрения JWT нет ничего неправильного в использовании симметричного ключа, это один из легальных способов.

Что касается «удобней» — это уже вопрос к вашей системе. Я могу придумать случаи когда использование HMAC будет удобней, но суть не в этом, а в том, что это один из нормальных методов подписи и верификации JWT.

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

эээ… а это что? «я лишь подчеркнул что это не правильно...», «вы не правильно пишите что application server должен знать секрет ...»

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

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

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

Зато симметричное шифрование гораздо быстрее асимметричного.
Все правильно в статье написано. В статье взят пример с HMAC'ом, а он на симметричном ключе. Симметричный ключ может быть безопасно расшарен между двумя участниками (сервер аутентификации и сервер приложений), например с использованием side-channel (на флешке) или по защищенному другими ключами каналу.
Вы пишете в первом сообщении, что сервер приложений сам проверить токен не может. Это полностью убивает сам смысл подписи токенов, так как сервер аутентификации может проверять валидность выданных им токенов и без криптографии, просто храня все выданные токены в БД.
Публичный ключ тоже требует защиты, правда не от узнавания, а от подделки. Поэтому и придумали PKI.

еще раз по порядку.
в случае если у вас несколько сервисов и один и тот же ключ используется во всех сервисах и доступен он между сервисами, если злоумышленник получит доступ к одному из сервисов, он сможет получить доступ к ключу, а с помощью ключа он сможет генерировать какие угодно токены, чем больше сервисов знает общий секрет, тем выше шансы на компрометацию.
В такой ситуации безопасней чтобы сервисы для валидации токена обращались к сервису авторизации и он уже проверял валидный он или нет, тем самым получив доступ к другим сервисам, они не смогут выпускать произвольные токены, а более правильно это ассимитричное шифрование, как человек тоже написал ниже, что есть приватный ключ и он известен в одном месте(в котором выдают токены), остальные сервисы знают только ПУБЛИЧНЫЙ ключ, с помощью которого могу проверять подписи, это публичный ключ можно хоть всему миру рассказать, главное что для генерация токена надо приватный ключ.

Хороший обзор, спасибо.


только сервер аутентификации и сервер приложения знают секретный ключ

Cекретный ключ они знают только если сервер аутентификации это и есть сервер приложения (т.е. это физически одно и то же приложение), если же они разные — то скорее будет использована пара private / public key. Вряд ли сервер аутентификации будет смело делиться со всеми серверами приложений своим приватным ключом.

Речь идёт о симметричной схеме подписи, с одним ключом.

Печально что многие не вдупляют… Автору респект ;)
По-моему, не сказано главного — чем оно лучше традиционных токенов.

Ответ: ресурсному серверу больше нет нужды стучаться в авторизационный сервер за проверкой валидности токена и его правами при каждом запросе от клиента. Он теперь в силах сам относительно дёшево проверить и валидность, и получить доп. информацию (например, права доступа) из самого токена. JWT гарантирует их аутентичность. Т.е. имеем упрощение серверной архитектуры и уменьшение нагрузки на авторизационный сервер.
В контексте данного ответа меня всегда интересовал вопрос: как быть, если токеном завладел злоумышленник и его нужно срочно сделать недействительным до истечения его срока годности? Или, другими словами, как реализовывать кнопочку на сайте «Завершить такую-то сессию» / «Завершить все сессии, кроме текущей»? Если сервер никуда не стучится, как он узнает, что токен недействителен?

UPD: ой, тред не читай @ сразу отвечай, ниже то же самое спросили
Надо делать короткое время жизни токена, несколько минут.
Если отдавать такой токен конечному пользователю, это будет тотальное неюзерфрендли. Перезагрузил комп после обновления винды — всё, заново логиниться? Разве что использовать его по схеме от fuCtor ниже, но тогда для большинства сайтов JWT оказывается бесполезен.
Конечному пользователю токен хранить не надо. Конечный пользователь получает и обновляет регулярно токен от authorisation sever.
JWT выглядит всё более и более бесполезным. Если в итоге всё равно надо логиниться каждые несколько секунд минут (refresh token ведь тоже однажды протухнет? Или я чего-то не понимаю?), то тогда почему бы не тупо посылать пароль (или другую информацию для логина) в каждом запросе, благо HTTPS это безопасно позволяет?)

Или существуют случаи, когда «authorisation sever» и сервер, читающий и проверяющий JWT — не один и тот же сервер? Я с таким ещё ни разу не сталкивался. (Не считая кучки микросервисов, спрятанных за некой единой точкой входа — тогда можно применить схему от fuCtor ниже, и JWT для пользователя опять бесполезен)
Мы используем oAuth2 JWT от MS Azure для доступа к нашим API. Все как часики и с пол пинка. Можно даже не использовать refresh token. MS поставляет JS библиотеку которая обходится без refresh token, если есть такая необходимость. Для например SPA все замечательно. Пока есть сессия — токен автоматически, невидимо для пользователя, обновляется.
Винда запустила обновление, все приложения закрыты минут десять — всё, сессия успела протухнуть, логинимся заново?
Если вы говорите об SPA, то естественно, если браузер был закрыт и сессии убиты, то пользователю заново надо будет пройти аутентификацию.
Вот это и грустно и основная причина, почему лично я не использую JWT
Хм, так а какая альтернатива? Вы предлагаете восстанавливать сессии? А как с безопасностью?
К JWT это вообще отношения не имеет. Если безопасность не проблема — восстанавливайте сессии с authorisation server по куки (или еще как нибудь) невидимо для пользователя и открывайте ему сразу страницу его банковского счета, например.
Банковские счета это отдельная история, о которой и говорить нужно отдельно, большинство сайтов не являются банками :)

От обычного сайта обычный пользователь будет требовать, чтобы его не разлогинивало. Из-за этого безопасность с короткоживущими токенами СНИЖАЕТСЯ: для постоянного автоматического релогина потребуется хранить пароль (или аналогичную инфу (refresh token?)) в открытом виде где-то рядом с токеном, и его с радостью уведёт злоумышленник вместе с токеном (mitm в эпоху повсеместного https почти нереален, скорее всего будет какой-нибудь RCE/XSS или тупо физический доступ к устройству). Придётся везде менять все пароли, похожие на утёкший. А вот если злоумышленнику попадёт классический долгоживущий токен, то достаточно будет просто удалить его из списка действительных.

Буду рад узнать, что я в чём-то не прав.
Окей, я придумал пользу от JWT: список недействительных токенов скорее всего будет намного меньше, чем список действительных, что позволит сэкономить место. Но больше как-то не придумывается (не считая банков, это отдельная история)
Хотя нет, так тоже не прокатит. Злоумышленник может удалить токен с места утечки (почистить localStorage, например), и тогда в список недействительных токенов добавлять окажется нечего. Придётся всё равно где-то хранить список действительных токенов. В общем, для меня JWT — штука бесполезная)
Идея refresh token — это идея oAuth2, а не JWT.
И как раз этот токен предназначен для того, чтобы пользователю не приходилось вводить пароль снова и снова.
Никто не мешает вам сделать JWT токен долгоживущим, если вы по какой-то странной причине считаете это нужным.
Никто не запрещает вам добавить проверку JWT токена в черном списке, как я сказал выше.
Разница и профит в том, что вам не надо напрягать сервер авторизации для такой проверки.
Но вообще говоря, короткоживущий токен рекомендуется в oAuth2 вообще и вне зависимости от механизма валидации.
Если честно, я с refresh token'ами работал мало и не до конца их понимаю, не могли бы вы пояснить — если злоумышленник утащит основной токен, то скорее всего где-то рядом он сможет взять и refresh token (если это не mitm), и тогда он просто будет продолжать получать новые токены по этому refresh token'у, что окажется эквивалентно простому долгоживущему токену; я что-то упускаю в описанной схеме или так и есть?

Алсо, я вроде где-то читал, что refresh token отдавать юзеру вообще нельзя, что лишь добавляет путаницы в моём понимании
refresh token не передается сервису и т.о. он более безопасный. Компрометация refresh tokenа сама по себе еще не обязательно приведет к возможности получения злоумышленником новых токенов. Зависит от того, какой механизм используется для передачи токена клиенту.
> Компрометация refresh tokenа сама по себе еще не обязательно приведет к возможности получения злоумышленником новых токенов

Вот это мне непонятно. Какой механизм может этому помешать? Передача рефреш токена, как бы она не просиходила, будет идти от клиента к серверу auth и в ответ на валидный refresh token auth server радостно создаст новый access token. Кроме условного «усиления refresh token» путем добавления туда чего-то типа source ip и/или агента или еще нечто в таком роде, я не вижу какой тут механизм помешает.
Токен может отправляться назад на предопределенный URL, который слушает клиент.
ничего не понял. кем отправлятся? что именно слушает клиент? что значит назад?

Насколько я себе представляю, этот рефреш токен должен быть у клиента и получит он его в момент login, вместе с access token, где его, теоретически можно украсть, например если есть физический доступ к компьютеру жертвы. Потом он (клиент) его будет время от времени посылать auth серверу для получения нового access token. Я не вижу как в этой схеме помешать злодею который завладел этим refresh token.
Зависит от вашего сценария.
1.Если у вас конфиденциальный клиент, то можно требовать отправки client_id и client_secret вместе с refresh token для получения нового токена доступа. Т.о. если злоумышленник не знает вашего секрета, то получить новый токен доступа он не сможет.
2. На публичном клиенте, если злоумышленник знает client_id и refresh token — то это все что ему нужно :-).
Срочно не получится. Выход один: делать время жизни JWT-токенов короткой и использовать refresh-токены.
Если действительно надо, то никто не мешает добавить в процедуру валидации токена проверку его наличия в каком нибудь централизованном черном списке, если это необходимо.
Это уже будет обращение в хранилище, от которого мы хотели отказаться. В таком случае ничего не мешает обращаться в это хранилище за обычными токенами.
Разница в том, что это будет обращение к внутреннему хранилищу, а не к внешнему серверу авторизации.
Но, да, я согласен, это чисто теоретический и излишне параноидальный сценарий.
Я считаю, что делать токен короткоживущим достаточно. Т.к. за то время, пока вы узнаете, что токен скомпрометирован и добавите его в некий список, он уже скорее всего устареет в любом случае.

Хранилище чёрного списка может быть гораздо более быстрое, вплоть до нахождения в оперативной памяти адресного пространства сервера приложений.

Ещё два:


  • блочить пользователя целиком до истечения токена
  • вести чёрный список
НЛО прилетело и опубликовало эту надпись здесь

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

Но в тоже время его можно использовать и в следующе схеме:
— есть два контура внешний и внутренний
— снаружи токен = oauth2 токен, со всеми плюшками, а внутри ходит JWT.
— при проверки oauth2 токена получаем короткоживущий JWT
— всё общение внутри уже идет используя именно JWT

Как результат на каждый запрос сервер авторизации запрашивается лишь раз, а остальные микросервисы довольствуются лишь данными из JWT, в принципе в подавляющем числе случаев достаточно UserID и набора прав.
либо вести базу инвалидных токенов (а это уже таблица в базе, сводящая на нет все преимущества токена с пейлоадом)

Т.к. размер этот базы на практике будет небольшим, а элементы в ней относительно короткоживущими (не сильно дольше времени жизни токена), то вполне можно сделать весьма быструю эффективную ее реализацию.

Если речь идёт именно о бане пользователя (а не отдельной его сессии), то просто отозвать права. Пользователь останется аутентифицированным, залогированным, но ничего защищенного проверкой прав в системе сделать не сможет.

У меня есть вопрос. Возможно глупый. Каким образом токен JWT защищен от перехвата третьей стороной?
Так же как и обычный токен (никак).
Каким образом токен JWT защищен от перехвата третьей стороной?

Использованием https.
НЛО прилетело и опубликовало эту надпись здесь
Отличие токена от пароля, в данном контексте, только в том, что токен короткоживущий.
Разве что привязка к IP-адресу. Всё остальное при желании можно подделать. Причём касается это не только JWT, но и любых других токенов и кук. Но так как пользователь часто меняет IP и привязка к нему тоже будет неюзерфрендли, в итоге я не знаю ни одной реализации (хоть JWT, хоть не JWT), которая была бы защищена от подобных вещей (ну, помимо короткоживущих токенов, которые так любит vlsinitsyn)
При чем тут «так любит»? Вы сами признали что альтернативы нет. IP фильтрация лишь дополняет механизм валидации, а не заменяет его. Это даже не вопрос JWT, а любого oAuth2 токена. Т.к. даже если вы будете валидировать токен через внешний сервер, результаты этой валидации обычно кэшируются на сервере на некоторое время сравнимое со временем жизни JWT.
Привязка к ip на самом деле не так уж и «неюзерфрендли», как вы говорите. И, если подумать, то будет правильным, если смена сессии и смена ip будет происходить в один и тот же момент времени.
К примеру, ip изменился, а сессия осталась та же. Почему изменился ip? Вариантов ответа несколько, часть из них совсем «неюзерфрендли», а даже скорее «хакераттакли». Поэтому привязка JWT к ip в несложных случаях повысит безопасность на достаточно много процентов.
Почему изменился ip?

Телефон поймал вайфай и подключился к нему, отключив мобильный интернет. А я набираю этот комментарий. Если в это время меня ВНЕЗАПНО разлогинит из-за всего лишь подключения вайфая — телефон с большой скоростью устремится в сторону пола.

Мне одному показалось что 'algorithm' в const algorithmSECRET_KEY = 'cAtwa1kkEy' лишний?
Спасибо, Luckyland. Это была опечатка.
Спасибо автору за статью, а комментаторам за комментарии.

TL;DR

Для понимания JWT раздел Структура JWT скорее вреден, чем полезен — он нужен только если кому-нибудь понадобится делать собственную реализацию, из-за слова payload и довольно большого объёма по сравнению с остальной частью статьи у меня сперва создалось впечатление, что подписываются все передаваемые данные, а не только лишь сам токен.

Статью можно сократить, раздел «Структура JWT» убрать под спойлер или заменить ссылкой на соответствующую статью в Википедии, и в самое начало жирным шрифтом вынести два комментария motomac:
Зачем это вообще нужно
По-моему, не сказано главного — чем оно лучше традиционных токенов.

Ответ: ресурсному серверу больше нет нужды стучаться в авторизационный сервер за проверкой валидности токена и его правами при каждом запросе от клиента. Он теперь в силах сам относительно дёшево проверить и валидность, и получить доп. информацию (например, права доступа) из самого токена. JWT гарантирует их аутентичность. Т.е. имеем упрощение серверной архитектуры и уменьшение нагрузки на авторизационный сервер.

И ответ на вопрос UncleAndy
У меня есть вопрос. Возможно глупый. Каким образом токен JWT защищен от перехвата третьей стороной?
Так же как и обычный токен (никак).

Кто придумал в этом контексте перевести claim словом «заявка», когда тут однозначное, уже подтверждённое сервисом аутентификации, «утверждение»?..

Или это только для меня слово заявка ближе по смыслу к слову запрос, к чему-то недоказанному, что ещё требует подтверждения?..

Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории