Рекомендации по обработке токенов JWT на стороне сервера [закрыто]


111

(создан из этого потока, поскольку это действительно вопрос отдельный, а не специфический для NodeJS и т. д.)

Я реализую сервер REST API с аутентификацией, и я успешно реализовал обработку токена JWT, чтобы пользователь мог войти в систему через конечную точку / login с именем пользователя / паролем, после чего токен JWT создается из секрета сервера и возвращается в клиент. Затем токен передается от клиента к серверу в каждом аутентифицированном запросе API, после чего секрет сервера используется для проверки токена.

Тем не менее, я пытаюсь понять передовой опыт того, как именно и в какой степени токен следует проверять, чтобы создать действительно безопасную систему. Что именно должно быть задействовано для «проверки» токена? Достаточно ли того, что подпись может быть проверена с использованием секрета сервера, или я должен также перепроверить токен и / или полезную нагрузку токена с некоторыми данными, хранящимися на сервере?

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

Это подводит меня к вопросам:

1) Должна ли проверка токена JWT ограничиваться проверкой подписи самого токена, полагаясь только на целостность секрета сервера, или с отдельным механизмом проверки?

  • В некоторых случаях я видел совместное использование токенов и сеансов сервера, когда после успешного входа в систему через конечную точку / login устанавливается сеанс. Запросы API проверяют токен, а также сравнивают декодированные данные, найденные в токене, с некоторыми данными, хранящимися в сеансе. Однако использование сеансов означает использование файлов cookie и в некотором смысле противоречит цели использования подхода на основе токенов. Это также может вызвать проблемы у некоторых клиентов.

  • Можно представить себе, что сервер хранит все токены, которые в настоящее время используются в кэше памяти или аналогичном, чтобы гарантировать, что даже если секрет сервера будет скомпрометирован, чтобы злоумышленник мог произвести «действительные» токены, только точные токены, которые были сгенерированы через конечную точку / login будет принято. Это разумно или просто избыточно?

2) Если проверка подписи JWT является единственным средством проверки токенов, а это означает, что целостность секрета сервера является критической точкой, как следует управлять секретами сервера? Считывать из переменной среды и создавать (случайным образом?) Один раз для каждого развернутого стека? Периодически обновляется или обновляется (и если да, то как обрабатывать существующие действительные токены, которые были созданы до вращения, но должны быть проверены после вращения; возможно, этого достаточно, если сервер хранит текущий и предыдущий секреты в любой момент времени) ? Что-то другое?

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


1
Есть отличные вопросы. Re: вопрос 2. У меня такая же проблема с ЛЮБЫМИ секретными ключами, хранящимися на стороне сервера. Если вы выполняете какое-либо хеш-сопоставление или асимметричное дешифрование, будь то подписание jwt или дешифрование информации cc, хранящейся в базе данных, у вас должен быть секретный ключ, доступный по коду на сервере. Так где, черт возьми, вы его храните ?? Вот лучший ответ, который я нашел: pcinetwork.org/forum/index.php?threads/… - вероятно, так же безопасно, как и для ключа jwt.
jbd

Что такое секретный ключ в токене jwt? Я думаю, что сам токен jwt является секретом. Или секретный ключ мог быть RSAPrivateKey privateKey??
kittu

3
Это было задано некоторое время назад, но, возможно, кому-то это пригодится. В моем случае у меня есть «секретный ключ» для каждого пользователя. Поэтому каждый раз, когда пользователь входит в систему, я генерирую этот секрет и сохраняю его вместе с записью пользователя в БД. Я проверяю токен, используя этот секрет. После выхода я очищаю это значение. Это автоматически делает недействительными другие токены, созданные ранее (это то, что мне нужно).
Нельсон Родригес

Ответы:


52

Я тоже играл с токенами для своего приложения. Хотя я ни в коем случае не эксперт, я могу поделиться некоторыми своими впечатлениями и мыслями по этому поводу.

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

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

По вашим вопросам:

1.) По моему ограниченному опыту, определенно лучше проверять свои токены с помощью второй системы. Простая проверка подписи означает, что токен был создан с вашим секретом. Хранение любых созданных токенов в какой-либо базе данных (redis, memcache / sql / mongo или другом хранилище) - отличный способ убедиться, что вы принимаете только токены, созданные вашим сервером. В этом сценарии, даже если ваш секрет просочился, это не будет иметь большого значения, поскольку любые сгенерированные токены в любом случае не будут действительными. Это подход, который я использую в своей системе - все сгенерированные токены хранятся в БД (redis), и при каждом запросе я проверяю, что токен находится в моей БД, прежде чем я его принимаю. Таким образом, токены могут быть отозваны по любой причине, например, токены, которые каким-то образом были выпущены в дикую природу, выход пользователя из системы, изменение пароля, изменение секрета и т. Д.

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


5
Для второго пункта вот хороший ответ: security.stackexchange.com/questions/87130/…
Bossliaw

1
Поскольку токены доступны в заголовке, что, если токен украден, и злоумышленник попытается войти в систему с этим токеном (зная адрес электронной почты пользователя)?
kittu

22
Если вы храните каждый JWT, то JWT не имеет преимуществ, и вы также можете придерживаться случайных идентификаторов сеансов.
ColinM

46

Вот несколько вещей, которые следует учитывать при реализации JWT в вашем приложении:

  • Поддерживайте относительно короткий срок жизни JWT и управляйте им на сервере. Если вы этого не сделаете, и позже вам потребуется дополнительная информация в ваших JWT, вам придется либо поддерживать 2 версии, либо подождать, пока срок действия ваших старых JWT не истечет, прежде чем вы сможете реализовать свое изменение. Вы можете легко управлять им на сервере, если вы только посмотрите на iatполе в jwt и проигнорируете его exp.

  • Рассмотрите возможность включения URL-адреса запроса в ваш JWT. Например, если вы хотите, чтобы ваш JWT использовался в конечной точке /my/test/path, включите поле, как 'url':'/my/test/path'в вашем JWT, чтобы гарантировать, что оно когда-либо использовалось только на этом пути. Если вы этого не сделаете, вы можете обнаружить, что люди начнут использовать ваши JWT на других конечных точках, даже на тех, для которых они не были созданы. Вы также можете рассмотреть возможность включения вместо этого md5 (url), поскольку наличие большого URL-адреса в JWT в конечном итоге приведет к тому, что JWT станет намного больше, и они могут стать довольно большими.

  • Срок действия JWT должен настраиваться для каждого варианта использования, если JWT реализуются в API. Например, если у вас есть 10 конечных точек для 10 различных вариантов использования JWT, убедитесь, что вы можете заставить каждую конечную точку принимать JWT, срок действия которых истекает в разное время. Это позволяет заблокировать одни конечные точки больше, чем другие, если, например, данные, обслуживаемые одной конечной точкой, очень чувствительны.

  • Вместо того, чтобы просто истекать JWT через определенное время, рассмотрите возможность реализации JWT, которые поддерживают оба:

    • N использований - можно использовать только N раз до истечения срока их действия и
    • истекает через определенное время (если у вас есть одноразовый токен, вы не хотите, чтобы он жил вечно, если не использовался, не так ли?)
  • Все сбои аутентификации JWT должны генерировать заголовок ответа "error", в котором указывается, почему аутентификация JWT не удалась. например, «просрочено», «не осталось использований», «отозвано» и т. д. Это помогает разработчикам узнать, почему их JWT дает сбой.

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

  • JWT должен включать идентификатор, подробно описывающий, какое приложение сгенерировало токен. Например, если ваши JWT создаются двумя разными клиентами, mychat и myclassifiedsapp, тогда каждый должен включать имя проекта или что-то подобное в поле «iss» в JWT, например, «iss»: «mychat»

  • JWT не должны регистрироваться в файлах журнала. Содержимое JWT может регистрироваться, но не сам JWT. Это гарантирует, что разработчики или другие пользователи не смогут захватить JWT из файлов журнала и сделать что-либо с учетными записями других пользователей.
  • Убедитесь, что ваша реализация JWT не поддерживает алгоритм «Нет», чтобы хакеры не создавали токены, не подписывая их. Этого класса ошибок можно полностью избежать, игнорируя «заголовок» вашего JWT.
  • Обязательно подумайте об использовании iat(выдано в) вместо exp(истечение срока) в ваших JWT. Зачем? Поскольку в iatосновном означает, когда был создан JWT, это позволяет вам настроить на сервере, когда истекает срок действия JWT, в зависимости от даты создания. Если кто-то пройдет через exp20 лет в будущем, JWT будет жить вечно! Обратите внимание, что срок действия JWT автоматически истекает, если ихiat находятся в будущем, но немного места для маневра (например, 10 секунд) на случай, если время клиента немного не синхронизируется со временем сервера.
  • Рассмотрите возможность реализации конечной точки для создания JWT из полезной нагрузки json и заставьте всех ваших реализующих клиентов использовать эту конечную точку для создания своих JWT. Это гарантирует, что вы можете легко решить любые проблемы безопасности с помощью того, как JWT создаются в одном месте. Мы не сделали это сразу в нашем приложении, и теперь нам нужно постепенно выпускать обновления безопасности на стороне сервера JWT, потому что нашим 5 различным клиентам нужно время для внедрения. Кроме того, сделайте так, чтобы ваша конечная точка создания принимала массив полезных данных json для создания JWT, и это уменьшит количество HTTP-запросов, поступающих в эту конечную точку для ваших клиентов.
  • Если ваш JWT будет использоваться на конечных точках, которые также поддерживают использование по сеансу, убедитесь, что вы не помещаете в JWT ничего, что требуется для удовлетворения запроса. Вы можете легко сделать это, если убедитесь, что ваша конечная точка работает с сеансом, когда JWT не предоставляется.
  • Итак, JWT, вообще говоря, в конечном итоге содержит какой-то userId или groupId и разрешает доступ к части вашей системы на основе этой информации. Убедитесь, что вы не разрешаете пользователям в одной области вашего приложения выдавать себя за других пользователей, особенно если это обеспечивает доступ к конфиденциальным данным. Зачем? Что ж, даже если ваш процесс генерации JWT доступен только для «внутренних» сервисов, разработчики или другие внутренние группы могут сгенерировать JWT для доступа к данным любого пользователя, например генерального директора компании какого-то случайного клиента. Например, если ваше приложение предоставляет клиентам доступ к финансовым записям, то, создав JWT, разработчик может получить финансовые отчеты любой компании! И если хакер так или иначе проникнет в вашу внутреннюю сеть, он может сделать то же самое.
  • Если вы собираетесь разрешить кэширование любого URL-адреса, содержащего JWT, убедитесь, что разрешения для разных пользователей включены в URL-адрес, а не в JWT. Зачем? Поскольку пользователи могут в конечном итоге получать данные, которым они не должны. Например, предположим, что суперпользователь входит в ваше приложение и запрашивает следующий URL:, /mysite/userInfo?jwt=XXXи этот URL кэшируется. Они выходят из системы, и через пару минут в ваше приложение входит обычный пользователь. Они получат кэшированный контент - с информацией о суперпользователе! Обычно это происходит реже на клиенте, а больше на сервере, особенно в случаях, когда вы используете CDN, например Akamai, и позволяете некоторым файлам жить дольше. Это можно исправить, включив соответствующую информацию о пользователе в URL-адрес и проверив ее на сервере, даже для кешированных запросов, например/mysite/userInfo?id=52&jwt=XXX
  • Если ваш JWT предназначен для использования как куки сессии, и должен работать только на ту же машину JWT была создана, вы должны рассмотреть вопрос о добавлении JTI полей к вашему JWT. По сути, это токен CSRF, который гарантирует, что ваш JWT не может быть передан из браузера одного пользователя в другой.

1
То, что вы называете created_by, уже существует в JWT и называется iss(эмитент).
Фред

да, хороший момент - я обновлю это ... спасибо!
Брэд Паркс,

8

Я не думаю, что я эксперт, но хотел бы поделиться некоторыми мыслями о Jwt.

  • 1: Как сказал Акшай, лучше иметь вторую систему для проверки вашего токена.

    a .: То, как я с этим справляюсь: я сохраняю сгенерированный хэш в хранилище сеанса с указанием срока действия. Чтобы проверить токен, он должен быть выпущен сервером.

    б .: Есть хотя бы одна вещь, которую необходимо проверить используемым методом подписи. например:

    header :
    {
      "alg": "none",
      "typ": "JWT"
    }
    

Некоторые библиотеки, проверяющие JWT, примут это без проверки хеша. Это означает, что, не зная, что ваша соль используется для подписи токена, хакер может предоставить себе некоторые права. Всегда следите за тем, чтобы этого не произошло. https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/

c .: Использование cookie с идентификатором сеанса бесполезно для проверки вашего токена. Если кто-то хочет перехватить сеанс пользователя лямбда-выражения, ему просто нужно будет использовать сниффер (например, wirehark). Этот хакер будет иметь обе информации одновременно.

  • 2: Это то же самое для каждого секрета. Всегда есть способ узнать это.

То, как я с этим справляюсь, связано с пунктом 1.а. : У меня есть секрет, смешанный со случайной величиной. Секрет уникален для каждого токена.

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

Если вы хотите обеспечить максимальную безопасность, не следует слепо следовать лучшим практикам. Лучший способ - понять, что вы делаете (я думаю, это нормально, когда я увижу ваш вопрос), а затем оценить необходимую безопасность. И если Моссад захочет получить доступ к вашим конфиденциальным данным, они всегда найдут способ. (Мне нравится это сообщение в блоге: https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )


Хороший довод в пользу уникального секрета для каждого токена, но как каждый раз создавать уникальный секрет? Я использую библиотеку
nimbus

1
возможно, используйте хэш-пароль вашего пользователя.
momokjaaaaa

1
«Если вы делаете что-то не так, как это делают другие люди, людям будет труднее найти выход через вашу безопасность». Для меня это звучит как «Безопасность сквозь неизвестность». Лучшие практики называются так, потому что они практически снижают наиболее распространенные риски.
Mnebuerquo 05

@Mnebuerquo Я полностью согласен с вами, парень, который написал, что не стоит доверять ;-)
Deblaton Jean-Philippe

1
Он прав, хотя и не следует слепо следовать лучшим практикам. Хорошо понимать, почему передовой опыт считается лучшим . В каждом решении по безопасности есть компромисс между безопасностью и удобством использования. Понимание причины означает, что вы можете принимать эти решения разумно. (Тем не менее, продолжайте следовать лучшим практикам, потому что ваши пользователи этого не сделают.)
Mnebuerquo,

3

Здесь много хороших ответов. Я объединю некоторые ответы, которые считаю наиболее актуальными, и добавлю еще несколько предложений.

1) Должна ли проверка токена JWT ограничиваться проверкой подписи самого токена, полагаясь только на целостность секрета сервера, или с отдельным механизмом проверки?

Нет, по причинам, не связанным с раскрытием секрета токена. Каждый раз, когда пользователь входит в систему с использованием имени пользователя и пароля, сервер авторизации должен сохранять либо сгенерированный токен, либо метаданные о сгенерированном токене. Думайте об этих метаданных как об авторизационной записи. У данной пары пользователя и приложения должен быть только один действующий токен или авторизация в любой момент времени. Полезные метаданные - это идентификатор пользователя, связанный с токеном доступа, идентификатор приложения и время, когда был выпущен токен доступа (что позволяет отозвать существующие токены доступа и выпустить новый токен доступа). При каждом запросе API проверяйте, содержит ли токен правильные метаданные. Вам необходимо сохранить информацию о том, когда был выпущен каждый токен доступа, чтобы пользователь мог отозвать существующие токены доступа, если их учетные данные были скомпрометированы, и снова войти в систему и начать использовать новый токен доступа. Это обновит базу данных с указанием времени, когда был выпущен токен доступа (время создания авторизации). При каждом запросе API проверяйте, что время выдачи токена доступа превышает время авторизации.

Другие меры безопасности включали не регистрацию JWT и требование безопасного алгоритма подписи, такого как SHA256.

2) Если проверка подписи JWT является единственным средством проверки токенов, а это означает, что целостность секрета сервера является критической точкой, как следует управлять секретами сервера?

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

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

Однако система для смягчения повреждения скомпрометированного секрета сервера и предотвращения хранения секретов в исходном коде включает ротацию секрета токена с использованием службы координации, например https://zookeeper.apache.org. Используйте задание cron для генерации секрета приложения примерно каждые несколько часов (независимо от того, на какой срок действуют ваши токены доступа), и отправьте обновленный секрет в Zookeeper. На каждом сервере приложений, которому необходимо знать секрет токена, настройте ZK-клиент, который обновляется при изменении значения ZK-узла. Сохраните первичный и вторичный секреты, и каждый раз, когда секрет токена меняется, устанавливайте новый секрет токена на первичный, а старый секрет токена - на вторичный. Таким образом, существующие действительные токены будут по-прежнему действительны, потому что они будут проверены на соответствие вторичному секрету. К тому времени, когда вторичный секрет будет заменен старым первичным секретом, все токены доступа, выпущенные с вторичным секретом, в любом случае будут просрочены.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.