Вход через незащищенную сеть

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

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

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

Сейчас у меня в голове следующее:

  1. Приложение запрашивает одноразовый одноразовый номер [ограниченный по времени и сгенерированный случайным образом]
  2. Приложение хеширует пароль пользователя: [используя одноразовый номер в процессе]

sha1(sha1([password]) + [nonce])

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

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

[resquestHashedPassword] == sha1([dbHashedPassword] + [nonce])

  1. Клиент продолжает работать с другими частями службы, используя предоставленный JWT.

Есть ли способ, которым я могу сохранить пароль пользователя в безопасности, имея возможность использовать хэш-соль в хранилище данных на стороне сервера?

В двух словах, это вопрос о том, как использовать как "Nonce", как часть аутентификации запросы и "Salt" для хранения паролей.

* ИЗМЕНИТЬ 1 *

Просто добавлю некоторые уточнения:

  1. Я знаю, что мы можем использовать HTTPS для шифрования передаваемых данных (и мы это делаем), однако HTTPS не является непроницаемым. Так что это не реальный ответ на вопрос.

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

  3. Я знаю о других формах нападения. Однако вопрос касается безопасности пароля пользователя, а не безопасности приложения/службы. Если мне не нужен пароль в открытом виде, зачем мне отправлять его по сети? Безопасность сети — это еще одна проблема, которой этот вопрос не касается.

  4. SHA1 это только пример.

* ИЗМЕНИТЬ 2 *

Что вы думаете об использовании имени пользователя (в нашем случае это его номер телефона, то есть 10 цифр, а 4 из них на самом деле не случайны) в качестве соли? У него, конечно, очень маленькая энтропия по сравнению со случайной солью, но это единственный способ, который я нашел до сих пор, который позволяет мне использовать оба метода. Или более безопасно полагаться на совершенство HTTPS и вместо этого вообще отказаться от идеи использования «Nonce» в хеш-функции?


person Soroush Falahati    schedule 03.10.2017    source источник


Ответы (3)


Во-первых, не сворачивайте собственную криптовалюту.

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

Правильный способ защитить это — использовать SSL/TLS. Как только безопасный туннель установлен, вы можете передать учетные данные «в открытом виде», полагаясь на шифрование, предлагаемое HTTPS.

person John Wu    schedule 03.10.2017
comment
Я понимаю другие формы атак, при которых злоумышленник может отправить неверные данные на наши серверы с помощью украденного сеансового ключа или токена. Однако меня больше волнует пароль пользователя, а не безопасность сервиса. - person Soroush Falahati; 03.10.2017
comment
Ага. Хакер также может выдать себя за сервер и предоставить любой одноразовый номер, который он хочет, даже один и тот же снова и снова для каждого пользователя, а затем увидеть свой выбор атаки (словарь, радуга) на хэшах. Ваша схема бесполезна без аутентификации и шифрования. - person John Wu; 03.10.2017

(Я предполагаю, что «запросы приложений» и «конечная точка API» означают, что мы смотрим на сообщения в отдельных соединениях, таких как HTTP, а не в одном двунаправленном соединении с собственным протоколом.)

В предлагаемой вами схеме есть ряд проблем:

  1. Слабый хеш в базе данных: вам нужно хранить sha1(password) на стороне сервера, чтобы это работало, а этого недостаточно. Вы можете исправить это, сохранив что-то, используя правильный PBKDF с солью, и отправив соль вместе с одноразовым номером клиенту.

  2. Слабый пароль на проводе. SHA1 ДЕЙСТВИТЕЛЬНО быстр. SHA1 дважды + соль не намного лучше. Это предотвратит атаки на радужные таблицы, но атаки по словарю против него по-прежнему тривиальны.

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

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

Рекомендация: см. аутентификацию, используемую в других местах, например AWS v4.

Как минимум, я бы изменил вашу схему на:

  1. Запросите уведомление + соль пароля, которая предоставляется для конкретного пользователя, для диапазона времени от настоящего момента до 5 секунд спустя.
  2. Send a request, which includes:
    • current time
    • имя пользователя
    • запрашивать содержимое/заголовки
    • подпись для HMAC(k, list_of(nonce, time, username, request data))
    • (где k равно PBKDF(password, salt))

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

person viraptor    schedule 03.10.2017
comment
Да, это правда, я имел в виду отдельные HTTP-запросы. 1, 2: Совершенно верно, SHA1 был просто примером. 3: я подписываю пароль пользователя одноразовым номером (а также отправляю его), поэтому изменение его на новый по-прежнему приводит к отклонению запроса. 4: Как наличие большого количества одноразовых номеров делает это проблемой? Они могут иметь столько, сколько захотят, однако я не могу придумать, как они могут неправильно использовать эти данные для расшифровки прохода пользователя. Важно добавить, что одноразовые номера являются случайными (в разумных пределах), и очевидно, что мы должны ограничить их временем. Но я понимаю, что этого не было в тексте вопроса. - person Soroush Falahati; 03.10.2017
comment
@SoroushFalahati Для 3 я имею в виду изменение запроса, а не аутентификации. Например, вы отправляете ["your_user","correct_auth_hash","do_this_thing"], и я меняю его в полете на ["your_user","correct_auth_hash","delete_the_database"]. Для 4 я не имею в виду, что вы можете расшифровать пароль. Если вы не привязываете хэш авторизации ко времени, я могу просто сохранить правильные хэши аутентификации для последующего использования — мне больше не нужно знать пароль пользователя. - person viraptor; 03.10.2017
comment
Теперь, после очистки этих частей, можно ли безопасно отправлять соль пользователю? Возможно, удастся извлечь все соли (поскольку мы еще не завершили процесс аутентификации [первый запрос]) - person Soroush Falahati; 03.10.2017
comment
@SoroushFalahati Я добавил рекомендацию, что нужно изменить, если вы действительно хотите это сделать, но это все еще плохая идея. Oauth2 и другие схемы существуют не просто так — используйте их, если можете. - person viraptor; 03.10.2017
comment
Спасибо, очень хорошая информация. Использование времени в запросе заставило меня вспомнить о клиентском одноразовом номере. Вы предложили это, основываясь на этой идее, верно? Кроме того, я отредактировал вопрос, включив в него тот факт, что весь разговор касается метода входа в систему, а остальная часть приложения действительно использует токены. Мы просто слишком чувствительны к этому первому запросу. - person Soroush Falahati; 03.10.2017

У меня есть предложение: если вы хотите учесть, что кто-то может проникнуть через HTTPS в какой-то момент времени, то я бы рекомендовал один раз предоставить симметричный ключ для конкретного пользователя и использовать его для шифрования учетных данных пользователя + метка времени перед отправкой по сети.

Для предоставления симметричного ключа на Fly DSKPP можно использовать https://tools.ietf.org/html/rfc6063

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

Но для этого потребуется проделать большую работу... вам нужно учитывать РИСК по сравнению с затратами.

person Atmanirbhar Bharat    schedule 03.10.2017