Лучшие практики обработки токенов JWT на стороне сервера

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

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

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

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

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

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

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

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

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

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


person JHH    schedule 29.05.2015    source источник
comment
Есть отличные вопросы. Re: вопрос 2. У меня такая же проблема с ЛЮБЫМИ секретными ключами, хранящимися на стороне сервера. Если вы выполняете какое-либо сопоставление хэшей или асимметричное дешифрование, будь то подписание jwt или расшифровка информации копий, хранящейся в базе данных, у вас должен быть секретный ключ, доступный коду на сервере. Так где, черт возьми, вы его держите?? Вот лучший ответ, который я нашел: pcinetwork.org/forum/index.php?threads/ -- вероятно, так же безопасно, как и для ключа jwt.   -  person jbd    schedule 22.01.2016
comment
Что такое секретный ключ в токене jwt? Я думаю, что сам токен jwt является секретом. Или секретный ключ может быть RSAPrivateKey privateKey ??   -  person kittu    schedule 26.02.2016
comment
Вопрос был давно, но может кому пригодится. В моем случае у меня есть секретный ключ для каждого пользователя. Поэтому каждый раз, когда пользователь входит в систему, я генерирую этот секрет и сохраняю его вместе с записью пользователя в БД. Я проверяю токен, используя этот секрет. После выхода я очищаю это значение. Это автоматически делает недействительными другие токены, созданные ранее (это то, что мне нужно).   -  person Nelson Rodriguez    schedule 06.10.2017


Ответы (5)


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

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

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

На ваши вопросы:

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

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

person Akshay Dhalwala    schedule 17.06.2015
comment
Для второго пункта вот хороший ответ: security.stackexchange.com/questions/87130/ - person Bossliaw; 08.10.2015
comment
Поскольку токены доступны в заголовке, что, если токен будет украден и злоумышленник попытается войти в систему с этим токеном (зная адрес электронной почты пользователя)? - person kittu; 27.02.2016
comment
Если вы храните каждый JWT, то от JWT нет никакой пользы, и вы можете использовать случайные идентификаторы сеансов. - person ColinM; 26.11.2017

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

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

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

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

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

    • N usages - can only be used N times before they expire and
    • истекает через определенное время (если у вас есть одноразовый токен, вы же не хотите, чтобы он жил вечно, если его не использовать, не так ли?)
  • Все сбои аутентификации JWT должны генерировать заголовок ответа «ошибка», в котором указывается, почему аутентификация JWT не удалась. например «истек срок действия», «использования не осталось», «аннулировано» и т. д. Это помогает разработчикам узнать, почему их JWT терпит неудачу.

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

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

  • JWT не должны регистрироваться в файлах журналов. Содержимое JWT может быть зарегистрировано, но не сам JWT. Это гарантирует, что разработчики или другие лица не смогут получить JWT из файлов журнала и сделать что-то с учетными записями других пользователей.
  • Убедитесь, что ваша реализация JWT не поддерживает алгоритм «Нет», чтобы хакеры не создавали токены без их подписи. Этого класса ошибок можно полностью избежать, игнорируя «заголовок» вашего JWT.
  • Настоятельно рассмотрите возможность использования iat (выдано в) вместо exp (срок действия) в ваших JWT. Почему? Поскольку iat в основном означает, когда был создан JWT, это позволяет настроить сервер, когда истечет срок действия JWT, в зависимости от даты создания. Если кто-то проходит через exp через 20 лет, JWT живет вечно! Обратите внимание, что срок действия JWT автоматически истекает, если их iat будет в будущем, но оставьте немного места для маневра (например, 10 секунд), если время клиента немного не синхронизировано со временем сервера.
  • Рассмотрите возможность реализации конечной точки для создания JWT из полезной нагрузки json и заставьте всех внедряющих клиентов использовать эту конечную точку для создания своих JWT. Это гарантирует, что вы можете легко решать любые проблемы безопасности, которые вы хотите, с помощью того, как JWT создаются в одном месте. Мы не сделали этого сразу в нашем приложении, и теперь нам нужно медленно выпускать обновления безопасности на стороне сервера JWT, потому что нашим 5 различным клиентам нужно время для реализации. Кроме того, сделайте так, чтобы ваша конечная точка создания принимала массив полезных данных json для создания JWT, и это уменьшит количество http-запросов, поступающих в эту конечную точку для ваших клиентов.
  • Если ваши JWT будут использоваться на конечных точках, которые также поддерживают использование сеансом, убедитесь, что вы не добавили в свой JWT ничего, что требуется для удовлетворения запроса. Вы можете легко сделать это, если убедитесь, что ваша конечная точка работает с сеансом, когда JWT не предоставляется.
  • Таким образом, JWT, как правило, содержит идентификатор пользователя или идентификатор группы и разрешает доступ к части вашей системы на основе этой информации. Убедитесь, что вы не разрешаете пользователям в одной области вашего приложения выдавать себя за других пользователей, особенно если это обеспечивает доступ к конфиденциальным данным. Почему? Что ж, даже если ваш процесс генерации JWT доступен только для «внутренних» служб, разработчики или другие внутренние команды могут создавать JWT для доступа к данным для любого пользователя, например. генеральный директор компании какого-то случайного клиента. Например, если ваше приложение предоставляет клиентам доступ к финансовым записям, то, создав JWT, разработчик может получить финансовые записи любой компании! И если хакер каким-либо образом проникнет в вашу внутреннюю сеть, он может сделать то же самое.
  • Если вы собираетесь каким-либо образом разрешить кэширование любого URL-адреса, содержащего JWT, убедитесь, что разрешения для разных пользователей включены в URL-адрес, а не JWT. Почему? Потому что пользователи могут в конечном итоге получить данные, которые они не должны. Например, скажем, суперпользователь входит в ваше приложение и запрашивает следующий URL-адрес: /mysite/userInfo?jwt=XXX, и этот URL-адрес кэшируется. Они выходят из системы, и через пару минут в ваше приложение входит обычный пользователь. Они получат кешированный контент с информацией о суперпользователе! Обычно это происходит реже на клиенте и чаще на сервере, особенно в тех случаях, когда вы используете CDN, например Akamai, и позволяете некоторым файлам жить дольше. Это можно исправить, включив соответствующую информацию о пользователе в URL-адрес и проверив ее на сервере, даже для кешированных запросов, например /mysite/userInfo?id=52&jwt=XXX.
  • Если ваш jwt предназначен для использования в качестве файла cookie сеанса и должен работать только на том же компьютере, для которого был создан jwt, вам следует рассмотреть возможность добавления jti в файл jwt. По сути, это токен CSRF, который гарантирует, что ваш JWT не может быть передан из браузера одного пользователя в другой.
person Brad Parks    schedule 29.05.2017
comment
То, что вы называете created_by, уже заявлено в JWT и называется iss (эмитент). - person Fred; 03.10.2017
comment
да, хороший момент - я обновлю это ... спасибо! - person Brad Parks; 03.10.2017
comment
Первый пункт полезен, если вам нужно поместить часто обновляемые данные в поля полезной нагрузки JWT в ваших вариантах использования (поэтому вам нужно будет регулярно обновлять токен). Я думаю, что недолговечный JWT просто делает его более безопасным. - person Han; 05.02.2021

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

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

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

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

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

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

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

  • 2: это одинаково для всех секретов. Всегда есть способ узнать это.

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

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

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

person Deblaton Jean-Philippe    schedule 02.10.2015
comment
Хорошо иметь уникальный секрет для каждого токена, но как каждый раз создавать уникальный секрет? Я использую библиотеку nimbus jwt. - person kittu; 27.02.2016
comment
вероятно, используйте хэш-пароль вашего пользователя. - person momokjaaaaa; 20.04.2016
comment
Если вы делаете что-то не так, как это делают другие люди, людям будет труднее найти способ обойти вашу защиту. Для меня это звучит как Security Through Obscurity. Лучшие практики называются так потому, что они на практике снижают наиболее распространенные риски. - person Mnebuerquo; 05.06.2017
comment
@Mnebuerquo Я полностью с вами согласен, парню, который написал это, нельзя доверять ;-) - person Deblaton Jean-Philippe; 06.06.2017
comment
Однако он прав в том, что не следует слепо следовать лучшим практикам. Полезно понимать, почему лучшие практики считаются лучшими. В каждом решении по проектированию безопасности существует компромисс между безопасностью и удобством использования. Понимание «почему» означает, что вы можете принимать эти решения разумно. (Однако продолжайте следовать рекомендациям, потому что ваши пользователи этого не сделают.) - person Mnebuerquo; 06.06.2017
comment
@Mnebuerquo Я был тем, кто написал этот ответ, который я обновил после вашего комментария. Мне, наверное, нужно было больше кофе, когда я писал это 2 года назад ;-) - person Deblaton Jean-Philippe; 06.06.2017

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

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

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

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

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

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

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

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

person skeller88    schedule 15.07.2017

IETF разрабатывает RFC в рабочей группе oAuth, см.: https://tools.ietf.org/id/draft-ietf-oauth-jwt-bcp-05.html

person SPoint    schedule 23.05.2019