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?
13 Answers
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 hashing it using irreversible salt. This 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 hash 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 this nonce 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.
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 :)
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.
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.
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
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 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 bluesh, 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 blueshish.
- Compare.
An attacker would have to attack the bluefish key using a random source and ciphertext. The task is not easy.
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.