Client side password hash versus plain text - security

I'm putting together an android client (and possibly in the future iOS, web portal, etc) and php mysql server. Server side I am currently using the PHPass library to hash and salt the incoming passwords.
Should I make the client send plain text passwords over HTTPS/SSL or should the client do some form of hashing first. For example should every client simply sha1 (or some other algorithm) every outgoing password?

Most websites will send the password plain-text over an encrypted connection SSL/HTTPS. Hashing the password client-side can be done, but the advantage is small and often client-side languages (JavaScrypt) are slow so you can calculate less rounds in the same time, what weakens the hash. In every case the server must calculate a hash as well to be safe.
The advantage is small, because if an attacker can do a ManInTheMiddle attack, he can also modify/remove the script (JS) which does the hashing. Only an encrypted connection with SSL/HTTPS can protect against a MITM attack, so you need SSL anyway.
In your case with an app, it looks slightly different. Because the user first has to install your software, there is no need to send a script to the client, so a MITM cannot modify this script. Moreover, the app can calculate the hash relatively fast (if it can run native code) and therefore can do enough rounds on client-side.
This is what i would do:
For easiness send the password plain-text over an encrypted SSL/HTTPS connection and calculate the slow BCrypt hash server side, as you do now.
Only if the load on the server grows too heavy, then you can move the calculation of the slow BCrypt hash to the client app. Still use HTTPS to send the hash, and then calculate an additional fast hash (e.g. SHA-256) on the server. This is more complex, because you have to exchange and store the salt separately.

Another disadvantage of hashing passwords on the client is that you cannot change the hashing algorithm or iteration count without also having to update your clients.
For JavaScript clients that is not a problem, but you cannot easily guarantee that your users will be on the most recent version of your native client.
So I would stick with sending plain passwords over HTTPS.

In the early days of HTTP, there was Digest authorization as an alternative to Basic authorization. Instead of the HTTP header
Authorization: Basic <credentials>
you would use
Authorization: Digest <credentials>
It was an algorithm that increased security by avoiding the password being sent as cleartext. This was in the days when TLS/SSL came at a performance cost so this was an alternative. However the algorithm meant the password had to be stored as cleartext on the server. So you had a choice of sending the password cleartext but having a hash on the server, or sending the password as a hash but having cleartext on the server.
Unsurprisingly, as martinstoeckli said in his answer, now that TLS/SSL is widespread and easy to implement, HTTPS is used instead. You can store the password as a hash on the server but not expose the plaintext password if it is intercepted by a MITM attacker.

Related

Authentication with salted password

I'm using scrypt to generate strong hashes of the password of the user. I want to log the user in, but don't want to send the password in plaintext over the wire, how do I check if the password is correct (without a roundtrip), since it is salted?
I'm having a client / server scenario. The client is an application on a desktop computer (not a website, nor http server).
How can I achieve this? I came only this far: I'm generating the the salt + hash on the client, form a mcf out of it and send it to my server. Save the mcf to the database. I haven't send the password, just the hash which is practically useless (since scrypt should be quite strong, and would require a few million years to reverse it).
How can I now log the user into my service, without sending the plaintext password to the server to compare it? I can't rehash it, since it would result in a different hash due to a different salt? I would need to send the salt to the client, hash the password, send the hash to the server, compare it, and send some authentication token back.
How can I achieve this? Is an authentication token actually secure? It can be simply used to impersonate anyone, I guess?
don't want to send the password in plaintext over the wire,
Good idea, but if the connection is not encrypted (something like SSL/TLS), then whatever you send is plaintext. If you hash a password client-side, and send it over the network, then THAT is the password. Some would say that there is no benefit here, but it does prevent the user from exposing their actual password, which they probably re-use on other sites. (read more here)
Ideally you would use something like SSL/TLS to encrypt the connection. I guess if that wasn't possible, using asymmetric encryption with certificates on the message itself that you are sending would be an ok way of re-inventing the wheel, but I am hesitant to recommend that without having a security person look over it. It's very easy to screw up, and the rule is never roll your own crypto scheme.
If you can't verify/invalidate/update the public key, then it is not a good scheme.
I would need to send the salt to the client, hash the password, send the hash to the server, compare it, and send some authentication token back
The salt isn't supposed to be super secret, but it's not great to just give it away like that, especially to unauthenticated users. The authentication token, hash, salt, etc can all be intercepted if the connection is not encrypted. Even if they couldn't, you didn't solve the problem of users creating accounts through this method (maybe you don't need to, but it is worth mentioning).
You have to use asymmetric encryption where only the server can decrypt the data.
There is no short answer to your question, because there are so many pitfalls that can happen if you do it wrong. But as Gray says, you do need TLS protection.
I have two sources that give you detailed explanations on the right way to do this if you want to do client side scrypt processing.
Method to Protect Passwords in Databases for Web Applications. If you do not want to understand all the rationale, just jump to section 4 to see the implementation (where PPF = your scrypt).
Client-Plus-Server Password Hashing as a Potential Way to Improve Security Against Brute Force Attacks without Overloading the Server.
They are slightly different solutions but based upon the same ideas, and either should be good enough for you.

How to correctly apply client side hashing of passwords?

I'm looking for an established security mechanism to apply client side password hashing in addition to server side password hashing. The client is either a web app or a mobile app. The communication between client and server uses HTTPS. The server should use bcrypt or a similarly secure password hashing algorithm.
Now, I don't want to send the unencrypted password from the client to the server in order to prevent insider attacks against the user. I want to make sure that none of our admins is able to get the original password, since -- as we all know -- most people re-use their password on multiple web sites or use a password scheme to derive passwords that are easy to remember.
Additionally, I would like to implement an additional two-factor-layer of security into the authentication protocol that makes use of a secret stored on the client in order to strengthen the overall security even more.
Are there any published and validated mechanisms that can be used for this situation?
EDIT
In my scenario, evil admins cannot control the code of the app. The server that is doing the authentication and that provides sensitive data is a REST server. The application code is delivered by a different server (for the web app) or via the AppStore (for the mobile app). And I am also thinking about scenarios where an attacker has only read-only access to an internal network segment that connects the HTTPS server with the application server. In most real world setups, SSL termination is done on a dedicated server.
In contrast to a website, your scenario with an app is well suited for client side hashing. The app already contains the code for calculating the hash, and the code cannot be intercepted/altered as this would be the case with JavaScript. Even more, you can relieve the server from the CPU intensive calculation.
These are the necessary steps for registration:
On client side, calculate the hash of the password, using a slow hash function with a cost factor. Appropriate algorithms are BCrypt, PBKDF2 or SCrypt. Most libraries will generate a random salt on their own and include it in the resulting hash-value.
Send the client-hash to the server (you already mentioned the SSL connection).
On server side, again calculate a hash of the sent client-hash. This time you don't need a salt and you can use a fast algorithm like SHA-256. This is safe, because the client-hash is already a very strong "password", it is not possible to forge the client-hash out of the server-hash.
Extract the parameters like salt, cost factor, and algorithm from the client-hash.
Store the server-hash and the parameters (salt, …) in the database.
These are the steps for login:
The client has to ask for the parameters (salt, …), stored in the database on the server.
The client calculates the hash of the password with the same parameters as by the registration.
Send the client-hash to the server.
The server again hashes the client-hash with the fast algorithm and compares it with the stored hash in the database.
This is a bit more complex than sole server-side hashing, because you have to take care of storing the parameters (salt, …) separately.

Is it acceptable to publicly expose salts?

I'm building a REST API for a mobile app. I need to handle authentication. As far as I could find, these are the best practices regarding password storing.
On the client side-register
Let user choose a password
Create a sufficiently long random salt from a strong random number generator
Append the the salt to the password and hash it.
Send the hash and salt to the server and store it in database
Server side-log in
Expose a method that returns user's salt for a given userid(email)
Client side-log in
User types in his userid and password
Retrieve the salt for the given userid
Append the the salt to the password and hash it.
Send the hash to the server, check that it's the same and authenticate user
Obviously the week point in this approach is that an attacker can see the salt for any user effortlessly. Is this ok? Obviously I can store the salt on the client side as well but if e.g. the user is logging in from a different phone he has to be able to retrieve the salt.
The alternative to this is to send the password in plaintext and handle all the salting and hashing on the server side. I would use HTTPS.
Which of these approaches is preferable? Is there a third one I'm missing?
Obviously the week point in this approach is that an attacker can see the salt for any user effortlessly.
No, the weak point is that your server is trusting the client. Here is MSDN's brief and pointed take on the situation:
You should never trust user input directly, especially if the user input is anonymous. Remember the two golden rules: never trust user input, and always check data as it moves from an untrusted to a trusted domain.
Source: Do Not Trust User Input Directly
Here you have user data moving from their untrusted client to your server. You need to verify their input on your server. There is no attack that you can prevent by performing the generation of the salt and the hashing of the password on the client. The ONLY way to secure the server-client communication is with secure handshake key exchange for asymmetric encryption. This occurs over HTTPS, and once the link is secure, then sending the password in plaintext is no less dangerous than sending the code to the client to perform the hashing client side.
If you do not secure the connection to the client with HTTPS, you are always vulnerable to a Man-in-the-Middle attack. Even though the password is nominally obscured when returning to your server, an attacker could instead impersonate your server to the client, sending an HTML form that skips the hash or sends the password in plaintext to their server as well.
Exposing the salt in your scenario is an additional security hole, but it is minor next to the glaring hole that would allow an attacker to get the password directly. I can explain this attack further if you desire, but it is a totally pointless attack unless the password itself is better secured; and once that happens, you have no need to expose the salt in the first place.
Responding to comments, it is true that salts can be made publicly known without reducing security if you are certain that they were generated correctly. For a salt, that means they are of sufficient length, sufficiently random, and in some schemes that they are cryptographically secure. The problem is that you allow the client to generate the salt, which means, I think we all agree now, that they can't be trusted. Maybe an attacker corrupted an external library you're relying on for PRNG. Maybe some user devices have an insufficient entropy pool. Maybe their device isn't secure against side-channel attacks. If you're making an Android App, there are certainly some very insecure devices out there. In Apple's walled garden, these risks might be tolerably low, but you can never eliminate them.
Like I said originally, however, these risks are minuscule next to the real dangers of your scheme. Namely, you're transmitting the hashed passed in the clear to your server. Even if you hash it further before storing it in your database, so a SQL injection can't just dump all the user passwords, you're still vulnerable to Man-in-the-Middle attacks. Unencrypted HTTP traffic can be observed and recorded by any router it passes through. Once an attacker observes a hashed password, they can re-send the same request to your server, and be authenticated incorrectly.
You should strictly go for the alternative, "to send the password in plaintext and handle all the salting and hashing on the server side. I would use HTTPS."
Of course the sentence above is highly contradictory: if you would be using HTTPS then the password would not be in plaintext. You should use HTTPS.
At the server you can store the randomly generated salt as plaintext in the database. You should not opt for a normal hash but a password hash - such as one generated by PBKDF2 - instead.
The salt is there if your DB gets stolen: the salt will make sure that identical passwords are not revealed to be identical. Furthermore the salt will make it impossible to use rainbow tables with precomputed password hashes.
Two important things to notice: for web clients you will have to rely on the trusted TLS certificate store for creating a secure TLS connection. Furthermore, your client may not have access to a good random number generator.

Should I hash the password before sending it to the server side?

I noticed that most sites send the passwords as plain text over HTTPS to the server. Is there any advantage if instead of that I sent the hash of the password to the server? Would it be more secure?
This is an old question, but I felt the need to provide my opinion on this important matter. There is so much misinformation here
The OP never mentioned sending the password in clear over HTTP - only HTTPS, yet many seem to be responding to the question of sending a password over HTTP for some reason. That said:
I believe passwords should never be retained (let alone transmitted) in plain text. That means not kept on disk, or even in memory.
People responding here seem to think HTTPS is a silver bullet, which it is not. It certainly helps greatly however, and should be used in any authenticated session.
There is really no need to know what an original password is. All that is required is a reliable way to generate (and reliably re-generate) an authentication "key" based on the original text chosen by the user. In an ideal world this text should immediately generate a "key" by salting then irreversibly hashing it using an intentionally slow hash-algorithm (like bcrypt, to prevent Brute-force). Said salt should be unique to the user credential being generated.
This "key" will be what your systems use as a password. This way if your systems ever get compromised in the future, these credentials will only ever be useful against your own organisation, and nowhere else where the user has been lazy and used the same password.
So we have a key. Now we need to clean up any trace of the password on the clients device.
Next we need to get that key to your systems. You should never transmit a key or password "in the clear". Not even over HTTPS. HTTPS is not impenetrable. In fact, many organisations can become a trusted MITM - not from an attack perspective, but to perform inspections on the traffic to implement their own security policies. This weakens HTTPS, and it is not the only way it happens (such as redirects to HTTP MITM attacks for example). Never assume it is secure.
To get around this, we encrypt the key with a once off nonce.
This nonce is unique for every submission of a key to your systems - even for the same credential during the same session if you need to send it multiple times. You can reverse said nonce (decrypt), once it arrives in your own systems to recover the authentication key, and authenticate the request.
At this point I would irreversibly hash it one last time before it is permanently stored in your own systems. That way you can share the credential's salt with partner organisations for the purposes of SSO and the like, whilst being able to prove your own organisation cannot impersonate the user. The best part of this approach is you are never sharing anything generated by the user without their authorisation.
Do more research, as there is more to it than even I have divulged, but if you want to provide true security to your users, I think this method is currently the most complete response here.
TL;DR:
Use HTTPS.
Securely hash passwords, irreversibly, with a unique salt per password. Do this on the client - do not transmit their actual password. Transmitting the users original password to your servers is never "OK" or "Fine". Clean up any trace of the original password.
Use a nonce regardless of HTTP/HTTPS. It is much more secure on many levels. (Answer to OP).
Since it's over HTTPS, it's definitely just fine to send the password without hashing (over HTTPS it's not plaintext). Furthermore, if your application is depending on HTTPS to keep it's content secure, then it's useless to hash the password before sending it over HTTPS (i.e. if an attacker can unencrypt the data on the wire, you're screwed anyways)
No, in fact this would be a vulnerability. If the attacker is able to obtain the hash from the database, then he could use it to authenticate without needing to crack it. Under no circumstance should a user or an attacker be able to obtain a hashes password.
The whole point of hashing passwords is to add an extra layer of security. If an attacker is able to obtain the hash and salt from the database using SQL Injection or an insecure backup then he has to find the plain text by brute forcing it. John The Ripper is commonly used to break salted password hashes.
Not using https is a violation of the OWASP Top 10: A9-Insufficient Transport Layer Protection
EDIT:
If in your implementation you calculate a sha256(client_salt+plain_text_password) and then calculate another hash on the server side sha256(server_salt+client_hash) then this is not a serious vulnerability. However, it is still susceptible to eavesdropping and replaying the request. Thus this is still a clear violation of WASP A9. However, this is still utilizing a message digest as a security layer.
The closest thing i have seen to a client-side replacement for https is a diffie-hellman in key exchange in javascript. However, this does prevent active MITM attacks and thus is till technicality a violation of OWASP A9. The Authors of the code agree that this is not a complete replacement for HTTPS, however it is better than nothing and better than a client-side hashing system.
Sending a hash over the wire completely defeats the purpose of the hash, because an attacker can simply send the hash and forget about the password. In a nutshell, a system that athenticates using a hash in clear text is wide open and can be compromise with nothing more than network sniffing.
The password in plaintext show never (not even when using HTTPS) leave the client. It should be irreversibly hashed before leaving the client as there is no need for the server to know the actual password.
Hashing then transmitting solves security issues for lazy users that use the same password in multiple locations (I know I do). However this does not protect your application as a hacker that gained access to the database (or in any other way was able to get his hands on the hash) as the hacker could just transmit the hash and have the server accept it.
To solve this issue you could of course just hash the hash the server receives and call it a day.
My approach to the issue in a socket-based web application I'm creating is that on connection to the client the server generates a salt (random string to be added before hashing) and stores it on the sockets variable, then it transmits this hash to the client. The client takes the users password, hashes it, adds the salt from the server and hashes the whole thing, before transmitting it to the server. Then it's sent to the server which compares this hash to the hash(hash in the DB + salt). As far as I know this is a good approach, but to be fair I haven't read a lot on the topic and if I'm wrong about anything I'd love to be corrected :)
Disclaimer: I'm by no stretch a security expert-- and I'm posting with the hope that others will critique my position as overly cautious or improvable and I will learn from it. With that said, I just want to emphasize that hashing when it leaves your client doesn't mean you get to don't have to hash on the backend before putting it in the database.
Do both
Do both because:
Hashing on the ride over helps cover vulnerabilities of transport, if SSL connection is compromised, they still can't see the raw password. It won't matter in terms of being able to impersonate authorized users, but it will protect your users from having their passwords read in association w/ their email. Most people don't follow best practice and use the same password for many their accounts, so this can be a serious vulnerability to your visitors.
If someone, somehow was able to read passwords from the database (this does happen, think SQL injection), they still won't be able to execute privileged actions impersonating users through my API. This is because of hash asymmetry; even if they know the hash stored in your DB, they won't know the original key used to create it and that's what your auth middleware uses to authenticate. This is also why you should always salt your hash storage.
Granted, they could do a lot of other damage if they had free rein to read what they want from your database.
I just want to emphasize here that if you do decide to hash the key before departure from your clients, that isn't enough-- the backend hashing is, imo, much more important and this is why: If someone is intercepting traffic from your client, then they will see the contents of the password field. Whether this is a hash, or plain text, it doesn't matter-- they can copy it verbatim to impersonate an authorized client. (Unless you follow the steps which #user3299591 outlines, and I recommend you do). Hashing the DB column, on the other hand, is a necessity and not at all difficult to implement.
Use HTTP Digest - it secures the password even over http (but best useage would be http digest over https)
Wikipedia:
HTTP digest access authentication is one of the agreed methods a web server can use to negotiate credentials with a web user (using the HTTP protocol). Digest authentication is intended to supersede unencrypted use of the Basic access authentication, allowing user identity to be established securely without having to send a password in plaintext over the network. Digest authentication is basically an application of MD5 cryptographic hashing with usage of nonce values to prevent cryptanalysis.
Link: http://en.wikipedia.org/wiki/Digest_access_authentication
If you want to see a "real life" use, you could look at phpMyID - a php openid provider that uses http digest authentication http://siege.org/phpmyid.php
.. or you could start from the php auth samples at http://php.net/manual/en/features.http-auth.php
Http digest rfc: http://www.faqs.org/rfcs/rfc2617
From my tests all modern browsers support it...
If you're looking to replace a clear-text password over HTTPS with a hashed password over HTTP then you're asking for trouble. HTTPS generates a random, shared transaction key when opening up a communication channel. That's hard to crack, as you're pretty much limited to brute forcing the shared key used for a (relatively) short-term transaction. Whereas your hash can be just sniffed, taken off-line and looked up in a rainbow table or just brute forced over a long amount of time.
However, a basic client-side password obfuscation (not hash) sent over HTTPS does have some value. If I'm not mistaken this technique is actually used by some banks. The purpose of this technique is not to protect the password from sniffing over the wire. Rather, it's to stop the password from being usable to dumb spying tools and browser plug-ins that just grab every HTTPS GET/POST request that they see. I've seen a log file captured from a malicious website that was 400MB of random GET/POST transactions captured from user sessions. You can imagine that websites that used just HTTPS would show up with clear-text passwords in the log, but websites with very basic obfuscation (ROT13) as well would show up with passwords that are not immediately of use.
Whether there's an advantage, and whether it's more (or less) secure really depends on implementation. There's arguably some advantage, but if you implement it poorly, you could definitely create a solution that is less secure than passing even a plaintext password.
This can be looked at from the perspective of two types of attacks-- one with access to the network traffic, and another with access to the database.
If your attacker can intercept the plaintext version of the network traffic, then seeing a hash of the password is more secure than seeing the password in plaintext. Although the attacker could still log in to your server using that hash, it would require a brute-force crack (sometimes pre-computed) of that hash to determine the password that might be useful on other systems. People should use different passwords on different systems, but often don't.
If an attacker gained access to the database, perhaps through a copy of a backup, then you'd want to ensure that one couldn't log in with only that knowledge. If, for example, you stored a hash salted with the login name as hash(login_name+password), and you passed that same hash from the client for direct comparison, then the attacker could pick a user at random, send the hash read from the database and log in as that user without knowing the password, increasing the scope of the breach. In that case, sending the password in plaintext would have been more secure because the attacker would need to know the plaintext in order to log in, even having a copy of the database. This is where implementation is key. Whether you send a plaintext password or a client-side hash of that password, you should hash that value at the server-side and compare that hash with the hash stored in the user record.
Concepts to keep in mind:
You "salt" a hash by mixing in some scope-unique value to your hash, typically row-unique. Its purpose is to guarantee uniqueness of hashes from each other even if the plaintext values they represent are the same, so two users with the same password would still have different hashes. It's unnecessary to treat a salt as a secret.
When authenticating, always hash on the server-side whatever value you pass from the client as a password (even if it's already hashed) and compare it with a pre-hashed value stored on the database. This may necessitate storing a double-hashed version of the original password.
When you make a hash, consider adding a server/cluster-unique salt to the hash as well as a row-unique salt to safeguard against matching any pre-computed hashes in lookup tables.
If you're connected to an https server the data stream between the server and browser should be encrypted. The data is only plain text before being sent and after being recieved. Wikipedia article
If you want to achieve the same reliability as when transferring over https, then yes - one option - the implementation of an asymmetrically encrypted channel at the ajax level.
If we are not talking about registration (for example, the first transmission of the password is always protected), then there are options.
For example,
The server generates a random string and generates a salt sends it to the user.
The user calculates a hash from his password and using this hash as
a key encrypts this random string with a blowfish, for example (there
is an implementation in JS for sure) and sends it back to you.
You, on your own, using the hash stored on the server, also encrypt this
random string with a blowfish.
Compare.
An attacker would have to attack the bluefish key using a random source and ciphertext. The task is not easy.
Isn't SSL/TLS replacing the nonce? I don't see added value of this since SSL/TLS also protects against Replay Attacks.
Ref.
https://en.wikipedia.org/wiki/Cryptographic_nonce
It would actually be less secure to hash the password and send it over a non-encrypted channel. You will expose your hashing algorithm on the client. Hackers could just sniff the hash of the password and then use it to hack in later.
By using HTTPS, you prevent a hacker from obtaining the password from a single source, since HTTPS uses two channels, both encrypted.

Does it make security sense to hash password on client end

If you were to hash a user's password prior to sending it across the line and leaving it in plain-text in memory, would this improve the security of the application?
I would assume this mitigates a small fraction of vulnerabilities by protecting the data stored in the clients memory. But really if we're worried about someone reading the client's memory there are probably bigger problems that we can't address.
There's something that doesn't feel right about hashing on the client's end.
Is password hashing on the client end a common practice? Are there any other advantages or disadvantages to doing it?
EDIT:
Given the communication channel is secure (SSL). Under what conditions would it be acceptable and worthwhile to use such an approach. I'm asking this because it was suggested by a "security professional" that I use such a scheme during some application functions.
No.
When the client sends something, whether it is P or H(P) or H(H(P)) anyone who intercepts this can simply resend the exact same thing, thus making any function like this equivalent to using the password directly.
That's why you should use a nonce; The server can give out some random garbage k and the client will calculate H(P,k) and send it to the server. HMAC is a popular implementation of this method.
Provided the server never accepts the same nonce twice, this is secure against a replay attack.
Sending a hashed password won't improve security on your site, as others have pointed out (since you accept a hashed password, all the bad guy needs to know is the hashed version). It's also not really secure, since the bad guy can presumably load your login page and examine the Javascript or Java deployed.
What it does do is prevents somebody watching the packets from being able to pull out a password, and that is moderately useful. Many people use the same password on multiple sites (I do it for all but the higher security sites), and therefore if you can get one password from them you can log into other accounts on other sites.
It also prevents the real password from being stored, even temporarily, on your site, and that may provide a little extra security if your site is compromised.
So, while I'd consider user-side hashing to be potentially a good things, it isn't worth going to much extra trouble.
And, as others have told you, don't roll your own security. There's far too many things that can go wrong. You won't notice them nearly as fast as a practiced bad guy will.
The hash is identical to the password from a security POV in the scenario you describe: if I intercept the hash, I don't need to know the password, I can just send the server the hash I intercepted.
Authentication protocols go to some length to avoid this problem; security is hard, and you are best off selecting and implementing a well-understood protocol rather than rolling your own.
If your traffic is going over SSL, you're safe from interception and hashing gives you little extra benefit.
Yes, you should.
IEEE had a data breach in which 100K emails and passwords were exposed from a weblog.
http://ieeelog.com/
Obviously, IEEE should not have exposed their weblog! But if they had hashed the passwords at the client side, this wouldn't have been nearly as bad.
As the first answer states, you should use a nonce. If you use a long enough nonce (e.g. 128 bits), you don't really need to worry about reuse, as the server will never ask for the same nonce twice (assuming correctly seeded CRNG, etc.).
No, hashing at the client does not protect the password 'completely'. When one opts to hash the password at the client, then the digest submitted to the server, essentially becomes the password. This is not a problem in itself if SSL is deployed.
However, this scheme ends up creating more problems than it solves. If the server were to compare the hash submitted by the client with a stored hash in the database without performing any further cryptographic operations (especially hashing the input data), then the password is stored in clear text for all practical purposes. Any person with access to the stored hash can re-submit it to the server and gain access to accounts.
In simple terms, if the submitted hash (which is the same as the submitted hash) were to leak via any other vulnerability within the application (via SQL injection, for instance) then the application has a vulnerability where in it protects the passwords inadequately.
If the underlying vulnerability must be fixed, then it is necessary to treat the submitted hash as a password in clear text, which should then be hashed (with a salt preferably) before comparison with a stored hash.
I think it makes sense in one circumstance; you don't want to even know the client's plaintext password. If you hash at the client side, then salt and iteratively hash that hash the same way you would a plaintext pw. Other than that, its kinda silly.
Just make sure that you are sending your password through a secure channel (SSL). If the client can have an application private memory read, then most likely they have bigger problems, like for example a keylogger.
You'd be much better off if you used the Secure Remote Password protocol (SRP). It was designed for this.
I can give you different kind of approach
If you have not SSL you can hash password on client side and again it hashed on server side using another hashing method and store them on database
and when user login with password do the same process and match double hashed password with stored hashes
Yes it makes sense to hash the password on the client side even if you use SSL but still you must also hash it again on the server side.
This makes sense especially in case of a mobile app. If you hash on the client side even with a "constant salt"/domain string it will be much better than sending a password in plaintext even if you use SSL. If you send plaintext passwords to the server then in case someone hacks your server he will receive password in plaintext. So adding additional pre hashing on client side protects the users and their password which they probably use also in other places.
You will probably find many posts saying that client side hashing is not needed but they usually are related to a web apps and do not consider mobile app case. In case of a web app if someone hacks the server he can replace the website and remove the client side hashing anyway so in such case there is not that big advantage. But for mobile apps an attacker cannot replace code of the mobile app so hashing on the client side has a lot of sense in such case.
You can check this link for a possible solution with hashing on both client and server side: https://medium.com/#harwoeck/password-and-credential-management-in-2018-56f43669d588
So yes, hash on both client and server side.
Hashing on the client side opens up another huge hole: you may expose the hashing algorithm. You don't say whether this is web-based (client=JavaScript) or thick-client, but you're giving them more information. Given the channel is secure, you don't have to worry about the clear text password being sniffed.
Besides, if your hashing algorithm requires a salt, you would be exposing your salt, which means if they ever got access to the database, they would be able to decrypt every password.

Resources