Comments 102
Неплохое начало, но статья как минимум мало имеет смысла без контекста.
JWT вообще лучше не использовать без причины. Если у вас монолит приложение, вам JWT не нужно.
Если у вас SPA и бекенд монолит, то куки лучше JWT.
Если у вас бекенд на микросервисах, то JWT тут отлично пойдут, но должны храниться в кукисах. Так же нужно понять как отзывать, проверять. Понимать разницу и применение в шифрованных, подписанных. JWT с PKI или HMAC.
Хранить в на стороне клиента JWT вообще бессмысленно, те же куки лучше делают это. Исключением есть OIDC implicit flow для мобильных устройств.
JWT очень специфичный инструмент, и должен быть использован для решения специфичных задач.
Конечно, микросервисы являються тем местом где лучше всего их применять, с другой стороны можно рассмотреть кейс, к примеру, сайта где API для SPA и для мобильного приложения общий.
В чем смысл использовать JWT, если для валидации токена всё равно нужно обращаться в бд?
Внутри закрытой сети авторизация в принципе может не использоваться.
Можете привести юзкейс, где JWT лучший способов аутентификации?
В чем смысл использовать JWT, если для валидации токена всё равно нужно обращаться в бд?
Как раз смысл в том, чтобы по-максимуму не нужно было. Отсюда проблемы и проистекают. JWT валидируется без участия сервера его выдавшего и просто так отозвать его уже ставится невозможно.
Проблема именно в том, что токен может быть скомпрометирован. Как вариант — отзывать. Но как проверить отозван ли? Был ли использован refresh token? Или refresh токен можно использовать бесконечно, пока не истечёт?
Можете привести юзкейс, где jwt оправдан?
Все случаи, которые я могу представить, либо не требуют аутентификации, либо преимущество jwt нивелируется обращениями к бд.
Можете привести юзкейс, где jwt оправдан?
Обычно в JWT запихивают всякие credentionals. И сервис, получив запрос с токеном, формирует ответ без обращения к сервису авторизации за разрешениями. Потому что все разрешения уже в токене.
Но именно юзкейс, где JWT оправдан (не требует доп валидации токена, к примеру отзыв) я не могу ни найти ни придумать.
Можете привести такой пример, где JWT не требует доп валидации и общение сервисов не происходит внутри доверенной сети?
А однажды делал принудительный разлогин в SPA — при бане пользователя, браузер по вебсокету получал команду разлогиниться. Да, можно заблокировать разлогин, можно токен перехватить, но опять же ИБ устроило.
Есть такой flow. Идея в том что account.company2 это некий внешний сервер который просто хранит логины пользователей как sso. account.company1 тут база с информацией о правах пользователя его группах которые могут использоваться some server, а также другими серверами. И без jwt account.company1 должен был бы делать дополнительный запрос к account.company2 для проверки пользователя.
Дабавить информацию о пользователе в account.company2 он нам не подконтролен.
Пользователь авторизуется на сервере account.company2
После успешной авторизации происходит редирект на Some Server с пайлоадом в виде jwt
Правильно ли я понял?
Если да, то в случае, к примеру, если пользователь меняет пароль (либо происходит блок пользователя) на сервере account.company2, то доступ к account.company1 все равно сохраняется с предыдущим валидным jwt, так?
А если Some Server доверяет и account.company1 и account.company2, то какой смысл использовать аутентификацию между Some Server и account.company1?
пользователь меняет пароль (либо происходит блок пользователя) на сервере account.company2, то доступ к account.company1 все равно сохраняется с предыдущим валидным jwt, так?
Как правило именно так. Поэтому и совет, делать access токены с малым временем жизни, чтобы при запросе нового можно было проверить, что пользователь заблокирован или у него поменялся пароль, и потребовать заново ввести пароль.
Из вашего же коммента refresh как правило хранят в бд. Тогда оверхед на использование refresh нивелирует преимущество access token.
refresh токен обычно нужен только для того, чтобы получить новый access токен. Если время жизни у последнего час, то раз в час примерно и будет обращение к БД. Access токена и его преимуществ это никак не касается.
Но если делать access токен со слишком малым временем жизни, то нам необходимо чаще обращаться к бд для валидации refresh.
И все равно остается вопрос о консистентности данных.
Это выигрыш в производительности за счет безопасности. В любом случае даже короткий TTL — это "слепой период", на протяжении которого мы будем доверять любым устаревшим данным в JWT. В любом случае нам нужна stateful-инфраструктура для refresh-токенов, при этом мы по-прежнему не сможем отозвать JWT в любое время.
Либо пробовать гибридные варианты. Ставить перед клиентом API gateway, который всегда валидирует токены через базу, а дальше передает их в сервисы, которые проверяют у них только подпись и доверяют содержимому. Все таки в современном интернете east-west трафика в разы больше, так что экономия все еще будет существенной.
JWT токены не стоит НИКОГДА отзывать. Они не задумывались для отзыва. Это антипатерн и проблемы с пониманием таких простых вещей как 10минутные jwt
JWT токены не стоит НИКОГДА отзывать. Они не задумывались для отзыва.
Совершенно верно. Если чувствуется необходимость в отзыве, вам определенно не нужен JWT. Если вас не устраивает протухание данных в 10 минут — то же самое.
JWT по определению предназначен для хранения кэшированных данных на стороне клиента, поэтому наследует все проблемы кеширования вроде инвалидации или несогласованности.
Это может быть совсем другая БД на совсем другом сервисе. Основные операционные сервисы либо вообще в базу для валидации токена не ходят, либо ходят в быстрый KV только для проверки не отозван ли токен.
refresh как правило хранят в бд
Как правило рефреш на клиенте в персистент хранилище, а токен доступа на клиенте в памяти приложения.
Правильно ли я понял?
Почти. Я немного флоу упростил Some Server получает не напрямую jwt а code который обменивает на токен у account.company2. Но в общем виде да.
Если да, то в случае, к примеру, если пользователь меняет пароль (либо происходит блок пользователя) на сервере account.company2, то доступ к account.company1 все равно сохраняется с предыдущим валидным jwt, так?
Да и это более чем утсраивает так как вобще блокировка аккаунта на company2 нежелательна.
А если Some Server доверяет и account.company1 и account.company2, то какой смысл использовать аутентификацию между Some Server и account.company1?
В account.company1 хранятся реальные права пользователя для Some Server. Тоесть в account.company2 храниться просто ID пользователя(естественно что там хранится больше данных, но они нам не интересны и мы неможем там менять и добалять данные о пользователе). А вот в account.company1 и хранятся основные данные пользователя. Но там не хранится ни пароль ничего.
company2 и company1 хотят иметь общую базу пользователей, но сервисы у компаний разные и чтобы каждая компания могла хранить разны права для своих сервисов нужны два разных account сервисы. Приэтом хочется иметь общую базу пользователей. Приэтом company2 уже имеет базу пользователей а company1 хотела бы эту базу пользователей использовать.
Я немного флоу упростил Some Server получает не напрямую jwt а code который обменивает на токен у account.company2
Т.е. jwt хранится у Some Server и не передается пользователю?
account.company1 не доверяет Some Server?
Да и это более чем утсраивает так как вобще блокировка аккаунта на company2 нежелательна.
А насчет смены пароля пользователя? Или при помощи чего происходит авторизация?
Т.е. jwt хранится у Some Server и не передается пользователю?
account.company1 не доверяет Some Server?
Да. Пользователю может не передаваться(смотря что мы под пользователем понимаем). Он ему впринципе не нужен(пользователю).
Что под доверием подразумевается?
А насчет смены пароля пользователя? или как происходит авторизация?
Вся авторизация происходит только на стороне company2.
Если нету куков у some server то идем авторизовываться если есть используем.
Поидее some server использует jwt тольо для получение данных из account.company1. Посе этого оне ему ненужен. Также account.company1 может залезть в свою БД и проверить инфу о токене. Может проверить время выдачи токена и если ему подсовывают токен с более старой датой то отвергать его.
EDIT:
Также токен может быть просто с коротким временем например полчаса
Что под доверием подразумевается?
То, что сервис может отправлять запросы без аутентификации. К примеру, может ли Some Server запросить данные о пользователе из account.company1 без аутентификации (отдать только id).
Поидее some server использует jwt тольо для получение данных из account.company1.
Может быть вы авторизовываете не то, что надо? Возможно вам нужно аутентифицировать Some Server на account.company1 (К примеру по ip)? Сделать так, чтобы Some Server мог получать инфу из account.company1 по любому пользователю?
Или выдавая jwt вы тем самым выдаете Some Server разрешение на получение данных о пользователе, id которого заложено в jwt?
Вы не отвечаете на мой вопрос про консистентность (про смену пароля) =)
Вы при помощи jwt решаете проблему доступа к данным определенного пользователя?
Т.е. если вы запрашиваете статичные данные с company2, то достаточно было бы любой электронной подписи.
Вам jwt по сути нужен только для подписи статичных данных?
Тогда в вашем flow Web Broser вообще не важен? =)
Важен приложение похоже по своей работе на pgadmin4.
Вы не отвечаете на мой вопрос про консистентность (про смену пароля) =)Проблемма решается коротким сроком жизни токена.
Т.е. если вы запрашиваете статичные данные с company2, то достаточно было бы любой электронной подписи.Посути да. Но так-как company2 использует jwt и к нему куча готовых пиблиотек и он удобен, то почему бы и не jwt.
Вам jwt по сути нужен только для подписи статичных данных?
Вы предоставляете данные с сервера company1 только в том случае, если был авторизован в company2?
Т.е. вы решаете проблему доступа к данным на сервере account1?
Сколько у вас живёт токен?
30 минут.
В итоге у вас получается брешь?
Т.е. в течении 30мин злоумышленник может использовать невалидный токен?
Предпологается что если токен утек то это уже более серъезная проблемма.
А еще JWT полезно для межсервисной аутентификации, когда инициатором вызова является сам сервис, а не пользователь, где хочется закрыть доступ, но не хочется изобретать велосипед. Чтобы не держать специальный открытый endpoint для этого используются теже самые токены.
А вот юзкейс (именно практика), вот тут я не могу ни найти примера, ни придумать (всё упирается либо в доп валидацию либо в ненужность аутентификации).
Можете привести именно пример использования?
Обычно токен вводится если внешнего периметра нет. То есть по смыслу локальные сервисы торчат во внешку.
Вы про jwt токен или впринципе про токен? =)
Если про jwt, то я не вижу смысла в jwt. Он не жизнеспособен просто из-за того, что не гарантирует консистентность. А чтобы он это гарантированно его нужно валидировать на инвалидность (на то, что его не отозвали), но тогда легче использовать токен в виде рандомной строки, т.к. не будет оверхеда на подписи
Дополнительное обращение к БД имеет место быть если нужно проверить что токен отозван, это не всегда нужно.
Самый простой пример: несколько сервисов, на страницах которых блок «профиль» в шапке сайта формируется без обращения к БД. Или пример микросервисов где каждый сервис будет знать ID, уровни доступа и т.п. данные пользователя без обращения к БД.
Можете описать подробнее пример с шапкой? Я не понимаю, что именно вы имеете ввиду. Откуда в данном случае был получен токен.
С микросервисами так же. Откуда они получают запросы? От доверенного источника? Внутри сети?
С микросервисами: делаем api gateway, который выполняет ровно две функции: проверяет актуальность токена и маршрутизирует запрос дальше. А то и одну, на первом проверять и отправлять запрос на роутер. Остальным сервисам уже актуальность проверять не нужно, если запрос пришёл от доверенного api gateway (в простом случае из защищенного периметра по IP, а так можно по TLS c клиентским сертификатом заставить микросервисы общаться друг с другом)
Соблюдение этих мер в купе с частой ротацией Access/Refresh токенов должно помочь обеспечить высокий уровень безопасности на сайте.
Наверное, никогда не смогу принять такие выражения в технических статьях:
- в купе с частой ротацией
- высокий уровень
Частый это сколько? 1, 2 или 10? В час, сутки или год?
Высокий это сколько?
Нельзя быть чуть-чуть беременной!
Уходили от кук и сессий и к ним же вернулись. Нормально так...
Ну по сути плюс-минус это сессии и есть, если в сессии хранить только ID юзера.
В случае отсутствия необходимости экономить на SSO получается очень сходно.
Кука + сессия: получаем ID сессии, вынимаем оттуда ID пользователя, вытаскиваем username и другие данные из базы для отображения профиля в UI.
JWT: получаем ID пользователя, вытаскиваем username и другие данные из базы для отображения профиля в UI.
Избежать можно если сунуть username и другие данные в JWT, но часть данных, как правило, не публична. Плюс добавляется проблема инвалидации токенов и вот это всё.
Кука + сессия: получаем ID сессии, вытаскиваем сессию из базы, вынимаем оттуда ID пользователя, вытаскиваем username и другие данные из базы для отображения профиля в UI.
fixed
вынимаем оттуда ID пользователя
Нельзя ничего вынимать из айди сессии. Все, что с ней дозволено делать, это проверить ее наличие в базе и получить из базы же соответствие айди сессии пользователю (сессия это строка в базе, а не сам айдишник). Иначе можно передавать какие угодно ID в сессиях и представляться другим пользователем.
Именно это хотел выразить. Очень часто начинают с JWT, вроде всё нормально, но после сталкиваются с проблемами:
- А забаненного модератора как отлогинить?
- Понадобилась ещё информация в токене. А что делать со старыми, где информации нет?
- Надо сделать никнейм изменяемым. А он в токене...
- Понадобились на UI данные, которые приватные (тут либо шифруют информацию в токене либо начинают комбинировать).
Я возможно чего-то не понимаю, но как при правильно настроенном CSP можно слить JWT из local storage на сторонний сервер?
А как быть, если токен выдаёт некий SSO-сервис и с ним клиенту нужно ходить на разные сервисы, расположенные на разных доменах? Например, те же микросервисы могут не иметь единой точки входа. Cookie с токеном на сторонние домены просто так отправить не получится.
Случай конечно немного экзотический, но всё же на облачных платформах иногда встречается.
Объясните пожалуйста, я наверное что-то не понял, но если мы можем хранить в куки, то чем JWT будет отличаться от идентификатора сессии?
Зачем он тогда нужен?
На сколько я понимаю, JWT нужен в тех случаях, когда у нас есть отдельный сервер авторизации и доступ к разным endpoints, так как JWT умеет хранить ещё и дополнительную информацию в зашифрованном виде.
Или я не правильно понимаю?
Чтобы микросервис узнал айди юзера по айди сессии, у микросервиса должен быт ь доступ к разделяемому хранилищу этих самых сессий — что нарушает изоляцию микросервисов.
А в JWT можно прямо юзер айди записать. Или сразу список ролей/пермиссий, выданных этому юзеру (очень часто сервису неважно, что там за юзер — важно знать, есть ли у него доступ к той или иной операции)
Если не говорить о куче сервисов, к которым можно получить доступ с помощью одного токена, то JWT это просто аналог кук, в которые пишется всё, что сервер не хотел бы хранить на своей стороне между запросами клиента, а хотел бы видеть в них, запросах, без обращения к другим сервисам и хранилищам с гарантиями того, что эти данные он сам и отдал когда-то клиенту. Единственный плюс JWT по сравнению с обычными самоподписанными куками в таком сценарии: унифицированный формат и алгоритм формирования и проверки этих JWT кук. Готовые либы, много информации и т. п. Добротный такой готовый велосипед.
Для мобилок описанные проблемы безопасности не актуальны, а вот засунутый в куки токен выглядит не очень дружественно.
Нормально мобилки с куками работают. По крайней мере, под Android я работал. Вполне себе.
1) Это не особо дружественный к мобилкам интерфейс
2) Для клиентов, отличных от веб-браузера такое понятие, как «куки» вообще лишено смысла.
3) Средний «мидл» мобильный разработчик на аутсорсе при виде чего-то, минимально отличающегося от джейсона крякает и требует предоставить ему «нормальное API», а не «это говно». Понятно что они там джунов как мидлов продают, но суть дела не меняет.
Для клиентов, отличных от веб-браузера такое понятие, как «куки» вообще лишено смысла.
Это часть спецификации HTTP, которую должны поддерживать все клиенты, а не только браузеры. Где-то в конце 90-х ещё можно было попасть на клиента, которые в куки не умел, приходилось самому парсить, хранить и формировать. Но это 20+ лет назад было.
Есть мнение, что если совершена XSS атака, то утечка JWT — не самая большая ваша проблема.
Затем sw перехватывает запросы с api прикрепляет к ним accessToken и отправляет на api. Fetch на sw нельзя подделать.
+ немного магии на проверки подлиности.
Нет никакой опасности, что localStorage может кто угодно прочитать. Если очень боитесь об этой ситуации, попробуйте использовать базу данных в браузере IndexDB. А обновления токена поместить в сервис воркер. Ах и да, используйте 5-10 минутные токены или посмотрите как эту проблему решил firebase в модуле auth. JWT создан так, чтоб хранитб в нем общедоступную информацию, которую может прочитать кто угодно. Если вы храните там закрытую информацию, то это проблема в использовании. Почитав комментарии понял, что есть много заблуждений и непониманий как использовать jwt правильно. Да и все, что вы передаете на frontend общедоступно, и подразумевает ваше согласие на то, что любой может прочитать и получить эту информацию
О хранении JWT токенов в браузерах