I don't know why so many sites don't do it, but I know that it is possible and works well since I have done it already. The server sends a random salt string in the login page which gets appended to the password in javascript in the browser and the SHA1 hash of the result gets then submitted back to the server for verification.
Here is my SHA1 implementation in javascript, that you may use if you like:
function sha1(msg)
{
function rotl(n,s) { return n<<s|n>>>32-s; };
function tohex(i) { for(var h="", s=28;;s-=4) { h+=(i>>>s&0xf).toString(16); if(!s) return h; } };
var H0=0x67452301, H1=0xEFCDAB89, H2=0x98BADCFE, H3=0x10325476, H4=0xC3D2E1F0, M=0x0ffffffff;
var i, t, W=new Array(80), ml=msg.length, wa=new Array();
msg += fcc(0x80);
while(msg.length%4) msg+=fcc(0);
for(i=0;i<msg.length;i+=4) wa.push(msg.cca(i)<<24|msg.cca(i+1)<<16|msg.cca(i+2)<<8|msg.cca(i+3));
while(wa.length%16!=14) wa.push(0);
wa.push(ml>>>29),wa.push((ml<<3)&M);
for( var bo=0;bo<wa.length;bo+=16 ) {
for(i=0;i<16;i++) W[i]=wa[bo+i];
for(i=16;i<=79;i++) W[i]=rotl(W[i-3]^W[i-8]^W[i-14]^W[i-16],1);
var A=H0, B=H1, C=H2, D=H3, E=H4;
for(i=0 ;i<=19;i++) t=(rotl(A,5)+(B&C|~B&D)+E+W[i]+0x5A827999)&M, E=D, D=C, C=rotl(B,30), B=A, A=t;
for(i=20;i<=39;i++) t=(rotl(A,5)+(B^C^D)+E+W[i]+0x6ED9EBA1)&M, E=D, D=C, C=rotl(B,30), B=A, A=t;
for(i=40;i<=59;i++) t=(rotl(A,5)+(B&C|B&D|C&D)+E+W[i]+0x8F1BBCDC)&M, E=D, D=C, C=rotl(B,30), B=A, A=t;
for(i=60;i<=79;i++) t=(rotl(A,5)+(B^C^D)+E+W[i]+0xCA62C1D6)&M, E=D, D=C, C=rotl(B,30), B=A, A=t;
H0=H0+A&M;H1=H1+B&M;H2=H2+C&M;H3=H3+D&M;H4=H4+E&M;
}
return tohex(H0)+tohex(H1)+tohex(H2)+tohex(H3)+tohex(H4);
}
This way you can of course also avoid to ever send the passwords over the net, even when the user chooses or changes his/her password: A random salt gets appended to the newly entered password in the browser and the hash of this gets transmitted to the server. If the password change is accepted the server needs to store the salt and the hash for the user account instead of the plain text password. The login page now uses two salts: the static one of the user account and a random one that is only valid for a single login attempt. The plain text password gets first hashed with the static user account salt and this hash is hashed again with the current login salt and this gets transmitted to the server.
Update: The response to this and other answers to this question shows that some further explanation might be helpful here:
What are the benefits of the suggested approach?
- It is meant to make it considerably harder to harvest passwords from the data that is transferred over the network, i.e. by using a sniffer. It does not make it impossible to obtain a weak password from the data that gets transferred but strong passwords are save against all attacks that are feasible today.
- It is easy to implement and requires no extra effort from the user, needs no special equipment on the users side and works with all browsers that support javascript. The users get a login page as they know it and as it is widely accepted.
- The clear text passwords never enter the web server which makes it much harder to obtain them in case the server or it's database gets compromised some day.
- It protects the passwords of all users of the website, whether they are aware of the problem or not.
How can it be attacked?
- It can of course be broken if the attacker is able to manipulate the the website and/or the javascript that is transferred to the user. The attacker can then simply remove the hashing to let the browser send the password in clear text and may do the hashing himself if he still wants to send the response back to the origin server. But if this happens much more is at stake then just the protection of reused passwords. I.e. the attacker can then as well spoof the entire website. One possible protection against this attack would be to use a secure transport protocol like SSL. While this would make the first 2 benefits obsolete since they can now be achieved already by the transport layer security it would still have the benefit that the plain text passwords never enter the web server.
Why is it secure
- The suggested approach uses a SHA1 cryptographic hash function together with a salt. While the SHA1 hash function is considered relatively weak (in theory) compared to hash functions that use more bits it is still entirely save against preimage attacks which would be required to obtain a unknown strong password from the data that gets transferred. There has been not a single successful preimage attack reported against SHA1 to this day on any hardware, not even against MD5 which is considered to be weaker then SHA1. There are reports of successful collision attacks but these are far less computing intensive than preimage attacks and and of no use at all here. Even if SHA1 would indeed become useless for this use case some day it would be no big problem to switch to another hash function in relatively short time but you can of course use already one of these from the beginning. The only disadvantage of longer hashes is that they also take longer to compute which can be a factor here since the hashing runs in javascript and you may want to use it also on less powerful devices like phones.
- The salt is a string of random data. I use 20 characters from a alphabet of 62 symbols [0-9a-zA-Z] which is 119 bits of random data that gets generated with a secure random generator. The salt protects the hash values against attacks with rainbow tables and all other forms of precomputed values which could be used to grab at least the weaker passwords from the network traffic with easily available computing power. Weak passwords are still vulnerable since they can be deduced from the hash and salt with dictionary attacks and brute force for relatively short passwords. But due the salt these attacks require considerable computing power for every attempt to find a single password.
Why is it not widely used?
- I think it's mostly because the users at large are not really aware of the problem and thus don't request more protection. They also won't see any immediate benefit which means a website can hardly gain some accountable advantage form protecting their users passwords this way.
- There is transport layer security which prevents against password sniffing and additionally allows to authenticate the server and protects the content against modification.