Давайте сделаем несколько заметок к предыдущим ответам.
Во-первых, вероятно, не самая лучшая идея использовать алгоритмы хэширования на стороне клиента. Если ваш пароль солен на стороне сервера, вы не сможете сравнить хэши (по крайней мере, если вы не сохраните хеш клиента в базе данных на одном из уровней хеширования с паролем, который совпадает или хуже). И вы не хотите реализовывать алгоритм хеширования, используемый базой данных на стороне клиента, это было бы глупо.
Во-вторых, обмен криптографическими ключами также не идеален. MITM может теоретически (учитывая, что на клиенте установлен корневой сертификат) изменять криптографические ключи и изменять его собственными ключами:
Исходное соединение (не считая tls) с теоретического сервера, который обменивается ключами:
Клиентские открытые ключи запроса> сервер хранит личные ключи, генерирует открытые ключи для клиента> сервер отправляет открытые ключи клиенту
Теперь в теоретическом атласе MITM:
Клиентские открытые ключи запроса> MITM генерирует поддельные закрытые ключи > Сервер хранит закрытые ключи, генерирует открытые ключи для клиента> MITM получает открытые ключи от исходного сервера, теперь мы Вы можете бесплатно отправлять наши поддельные открытые ключи клиенту, и всякий раз, когда от клиента поступает запрос, мы расшифровываем данные клиента с помощью поддельных ключей, изменяем полезную нагрузку (или читаем ее) и шифруем с помощью исходных открытых ключей > MITM отправляет поддельные открытые ключи клиенту.
В этом смысл доверенного сертификата CA в TLS, и именно таким образом вы получаете сообщение от браузера, если сертификат недействителен.
В ответ на ОП: по моему скромному мнению, вы не можете этого сделать, потому что рано или поздно кто-то захочет напасть на пользователя из вашей службы и попытается нарушить ваш протокол.
Однако вы можете реализовать 2FA, чтобы люди никогда не пытались войти с одним и тем же паролем. Остерегайтесь атак воспроизведения, хотя.
Я плохо разбираюсь в криптографии, поправьте меня, если я ошибаюсь.