Подтвердить что ты не робот

Рекомендации по серверной обработке токенов JWT

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

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

По всем вопросам:

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

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

Ответ 2

Вот несколько вещей, которые следует учитывать при реализации 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 использований - можно использовать только N раз до истечения срока годности и
    • истекает через определенное время (если у вас есть одноразовый токен, вы не хотите, чтобы он жил вечно, если не используется, не так ли?)
  • Все ошибки аутентификации JWT должны генерировать заголовок ответа "ошибка", в котором говорится, почему аутентификация JWT не удалась. например, "истек срок действия", "не осталось использований", "отозван" и т.д. Это помогает разработчикам понять, почему их JWT дает сбой.

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

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

  • JWT не должен быть зарегистрирован в файлах журнала. Содержимое JWT может быть зарегистрировано, но не сам JWT. Это гарантирует, что разработчики или другие не смогут извлечь JWT из файлов журнала и сделать что-то для учетных записей других пользователей.
  • Убедитесь, что ваша реализация JWT не позволяет использовать алгоритм "Нет", чтобы хакеры не создавали токены, не подписав их. Этого класса ошибок можно полностью избежать, игнорируя "заголовок" вашего JWT.
  • Настоятельно iat использовать iat (выданный в) вместо exp (expiry) в ваших JWT. Зачем? Поскольку iat основном означает, когда был создан JWT, это позволяет вам настраивать на сервере, когда истекает срок действия JWT, основываясь на дате создания. Если кто - то переходит в exp, что 20 лет в будущем, 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 не может быть передан из одного пользовательского браузера в другой.

Ответ 3

Я не думаю, что я эксперт, но я хотел бы поделиться некоторыми сообщениями о 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 с идентификатором сеанса не было бы полезно для проверки вашего токена. Если кто-то хочет захватить сессию лямбда-пользователя, ему просто нужно будет использовать сниффера (например: wirehark). Этот хакер будет иметь одновременно информацию.

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

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

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

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

Ответ 4

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

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

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

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

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

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

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

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