AppSec Blog

An Encrypted Password Experiment

Jason's blog post ("How Not to Do Website User Registration") certainly attracted a lot of comments! I think articles like this exceed my expectations about this blog. Back before we had "blogs", we had the Internet Storm Center diaries, which are still going strong. I always felt that the best diaries are the diaries that don't have all the answers, but diaries that stimulate discussion and feedback.

With that comes the need to sometimes step back and consider that I don't have all the answers. In particular, we have to keep in mind that we don't secure websites just for themselves, but we secure websites so they can fulfill a function. 32 characters random passwords, which have to be changed daily, are just not the solution. I am always interested in ways to make software more secure without impacting usability. The way I sometimes put it: "Security is not about preventing a breach, security is about staying in business".

Now how does this apply to website registration and encrypted passwords? A dilemma I always had: I hash all my passwords in the database. I use SSL to transmit the password and cookies. But I hardly ever encrypt the password itself as it is transmitted from the browser to the server. You may say: Wait! We got SSL for that! Right, but remember SSL is just protecting the password. The website still ends up processing the clear text password. What is needed is a way to encrypt the password on the browser before passing it to the server.

The Internet Storm Center is one site that I like to use for experiments like that. However, the only way I know how to encrypt passwords is to use Javascript. I also have a rule not to force users to turn on Javascript. I may use it for the usability improvements, but the site has to work with Javascript turned off. I know this is an anachronism in today's Web 2.0 world, but you should see some of the hate mail we get just for using cookies.

HTTP actually has a decent way to encrypt passwords, called Digest authentication. But it has other problems and is not really an option for many sites.

Here is the solution I came up with: Make it optional. If Javascript is enabled, it is used to encrypt the password and the original clear text password is not sent. If Javascript is not enabled, the password is sent "as before".You can check out the Javascript code at https://isc.sans.org/login.html .

I am using a SHA1 hash. Sadly, Javascript doesn't have a SHA1 function natively, so I am using an implementation of the SHA1 algorithm in Javascript that appears to work pretty well (see http://isc.sans.org/js/sha1.js). The database stores the sha1 hash of e-mail address and password (sha1(email+password)). I could just pass this hash, but I well, since I hash the password anyway I want to do it right and avoid reply/"pass the hash" style attacks. So the server sends a "nonce" first. The nonce is a random value. The client now appends the nonce to the password hash and hashes it again. So the final hash function is:

sha1(sha1(email+password)+nonce)

The server stores the nonce in a session, and can do the same calculation using the password hash stored in the database.

This way the server is only exposed to the original password during the account creation and whenever the password is changed. You may ask why anybody would care about their ISC password... well, maybe if they use the same password for their online banking, I just don't want to touch it. Information becomes a liability pretty quickly in these cases and sometimes you have to protect the users from themselves.

So here my questions:

- Is this effort worth it? Am I fighting a real threat or am I just an obnoxious security fanatic?
- Are there other solutions?
- What did I do wrong? Any holes in this scheme? (I do consider sha1 good enough for this case)

[digg=http://digg.com/security/Javascript_Password_Encryption_for_Web_Applications]

9 Comments

Posted May 28, 2009 at 1:31 PM | Permalink | Reply

Matt K

This is nice! One question''"if it's optional (when js is enabled), are you creating two classes of password (javascript/hashed-transmission, and plaintext-transmission)? If so, if a user uses js to initially create the password, are they then required to have js enabled later to authenticate or change their password?

Posted May 28, 2009 at 2:27 PM | Permalink | Reply

Robert

maybe if they use the same password for their online banking" maybe is a huge word. Doesn't mean they are. I think your password should reflect the information your looking at for instance my gmail password is not as difficult as my online banking password. Also, I use a cleaner after all my internet exploring.

Posted May 28, 2009 at 2:28 PM | Permalink | Reply

Julien Touche

With html5/xforms1.1 [1], you can use digest() function.
Sadly, there is no real support for most browsers now.
This kind of function is useful to protect against form-grabbing attack. For now, we can only rely on javascript obfuscation with a session key.
[1] http://www.w3.org/TR/2007/CR-xforms11-20071129/#fn-digest

Posted May 28, 2009 at 2:49 PM | Permalink | Reply

sw0rdfi5h

I think this method will suffice considering its intended use. If you are looking at a more secure transmission use java applets, but if you are looking to cater to a greater audience share the presented method is best.

Posted May 28, 2009 at 4:02 PM | Permalink | Reply

Scott

One of the reasons for storing a hash of the password in the database is so that someone who ends up with a dump of the database (or part of it) cannot use the stored values to log in.
Using sha1(sha1(email+password)+nonce) as proof of knowledge of the password effectively makes sha1(email+password) the password ''" and you're storing that in the database.
Someone who ends up with a dump of the database (or parts of it) can use the stored values to sign in without knowing the password.
Also, if you're trying to protect against disclosure of plaintext passwords when the server is compromised (since SSL is already protecting the channel), can't the attacker just remove the Javascript from the login page and then get the plaintext passwords anyway?

Posted May 30, 2009 at 4:15 PM | Permalink | Reply

Chris Adams

It's a neat experiment but I'll second Scott's observation: largely ineffective since almost any plausible attack scenario would simply result in the attacker breaking the JS. I'd much prefer simply using a federated login system to break the assumption that every site needs to get a password from you or working to improve the browser UI for http digest mode since that's widely supported and doesn't require the user to audit JS for each site (which, realistically, is impossible for the foreseeable future).

Posted May 31, 2009 at 11:26 AM | Permalink | Reply

Javier Aroche

The forum software, vBulletin, does something like this, but with md5().

Post a Comment






Captcha


* Indicates a required field.