Feature Proposal: Use Javascript to avoid passing the password in the clear.

Motivation

Clearly the best solution for login security is to use SSL, however it can be complicated to set up, and is not necessarily supported on hosted sites. Our default Template authentication is very insecure. Regardless of the hashing used on the server, passing the password in the clear negates any attempts at stronger security.

Description and Documentation

Use Javascript to exchange a password hash to avoid exposing the password to casual sniffing.

One big mistake is to implement security designed by an amateur. And I'm clearly not an expert. But we should able to do something using other available libraries to at least improve the current situation.

What this does:

  • Avoid passing the password in the clear
  • Avoid passing the raw password hash in the clear
  • Prevent replay attacks of the login exchange.
  • Use stronger hash
  • Use well known algorithms - no "security through obscurity".

What it is not:

  • It's not an SSL exchange and is not an attempt at perfect security. Use SSL if that is needed. This is an attempt to improve our default login.
  • It won't hide the userid. It is still passed in the clear.
  • It is not intended to secure Foswiki, prevent session hijacking, or other attacks other than password interception.

Login flow:

  1. <-- Client POSTs bin/login?user=userid;protect=yes
    • what's protect=yes ? What happens if i say protect=no ? C.
    • I guess making it optional is a rather dumb idea. Though I suppose if the client doesn't care to protect their password, it might make handling things like BuildContrib a bit easier. - This probably needs to be a configuration parameter instead - require/allow/refuse protected login. A bit similar to StrikeOne. G
  2. --> Foswiki recovers salt from .htpasswd entry, and returns { Nonce, UserSalt }
    • If userid does not exist, generate a random salt so that valid / invalid id's cannot be fished
    • (I can't think of how to do this without revealing the salt, and I can't find where this is considered weak. The other choice would be to store the plain text password on the server, then the client doesn't need the salt.) * Revealing the salt makes a dictionary attack more credible, I guess. But there are so many other holes that this one isn't going to sink the boat on its own. C.
    • Need to consider whether or not to handle non-salted hashes. (sha1, htdigest-md5).
  3. Client performs:
    • UserSalt could be examined to detect algorithm used,
    • calculate the password hash from the entered password. (This hash is NOT sent to the host)
    • calculate a second hash using the {Userid, Nonce, and password hash} (avoids passing password hash in clear)
  4. <-- Client returns { Nonce, new-hash }
  5. Server performs:
    • Nonce used to continue the transaction
    • Server calculates hash of { Userid, Nonce, stored-password-hash }
  6. If calculated hash == client submitted hash, login is permitted.
All in all, sounds a lot like strikeone. Code re-use should be possible. Right - I assume the major difference is identifying the user and mixing in the password into the hashing.

Other changes

Implement the bCrypt hash for server side password storage.

Libraries planned for use:

Other references:

Examples

Impact

%WHATDOESITAFFECT%
edit

Implementation

I've completed the integration of CPAN:Crypt::Eksblowfish::Bcrypt into Foswiki::Users::HtPasswdUser complete with unit tests and auto-recognition of bcrypt hashed passwords. This could be committed to core as an optional form of encryption even if this proposal is rejected.

-- Contributors: GeorgeClark - 03 Jan 2012

Discussion

  • Worth doing?
  • Anyone want to assist with the Javascript / JQuery implementation? Based on JSON?
  • Should this be an extension of bin/login or a new rest handler?
  • Change password could do something similar, but the password hash would have to be passed in the clear.
  • If client is going to detect the hash, we either have to send down all supported algorithms initially in the javascript request, or have the client dynamically fetch the algorithm once the required one is determined.
  • Could we use the same mechanism for protecting configure?
-- GeorgeClark - 03 Jan 2012

I like this a lot. Vote, vote.

Alas, I am no security expert at all, even less than you George. I could help with the js code as soon as I see it and have way to test it myself for sure.

Depending on how much the original login and passwd classes need spaghetti, it might be better to make the secured variants separate login+passwd classes of their own. The insecure ones should be deprecated then and removed in foswiki-3.0 or so.

-- MichaelDaum - 04 Jan 2012

I'm no security expert either, but I implemented the strikeone so I suppose I'm more qualified to comment than some. I considered password encryption when I was working on template auth before, but decided against it because to provide additional security required a complex implementation. Some points to consider:
  1. Simply hashing a password achieves nothing, if the hash can be easily decoded. The same applies to any encryption where the key is sent in plain. Security "by obscurity" is no security, it's just stupid.
  2. Because the algorithm at some point is going to be passed to the browser, to encrypt passwords effectively requires PKE. This is quite a complex thing to stuff in JS in a browser.
  3. Even if you have PKE'd passwords, to prevent login replay requires a strike-one-like use-once token.
  4. Is it really worth bloating the browser for this? When it is so easy to implement https?
-- CrawfordCurrie - 04 Jan 2012

To your points:
  1. Simply hashing. Agreed. The reason to use a well-known hash like bcrypt with salt is to use an implementation that is reasonably resistant to dictionary and other attacks. This is discussed at length in the "yorick" link I provided. And I don't believe I proposed any "security by obscurity" at all. If you need to hide any aspect of the algorithm, then you are doing something wrong. It's why I have no reservations about discussing this in public.
  2. The algorithm is not passed. The password or password hash is never passed. It is not encrypted. It is hashed with salt. A well designed salted hash cannot be reversed in a reasonable amount of time. There are various brute force attacks, but is it really worth spending years of cpu to calculate the dictionary for one specific salt?
  3. Agreed. The "Nonce" I referred to is essentially a use-once token.
  4. Well, it's not really bloating the browser. It's one topic - the login template - that would need to reference the bcrypt and other crypto javascript libraries ... if enabled. I think the availability of JS based crypto libraries is what makes this possible to consider.
And HTTPS is not really easy:
    • It needs a certificate. For very small organizations, that alone is a rather significant hurdle. You either use a free one like CACert and end up with browser cert security warnings that scare away users (first-hand experience. Users are so virus paranoid, that they see a warning and go elsewhere). Or you pay really big bucks to purchase a cert from a CA trusted by the browsers. and pay ... and pay.
    • It makes virtual hosting difficult and/or costly, due to premium certificates. (first-hand experience here too)
    • It can be complicated to set up for the person unfamiliar with apache, or pki.
    • It adds considerable server workload. The ISAKMP/IKE and PKI calculations done for each new SSL connection is expensive. Or you redirect the login page to https, and that gets even more complicated.
    • I'm assuming that for web hosting sites,. HTTPS is a premium service.
I run a couple of small sites - almost nothing really needs encryption, nothing confidential, nothing financial, ... But sending a password in clear text is really stupid as well. So the question is - for this category of user do we do nothing, and pass the passwords totally in the clear? Tell them too bad - wanna play, you "pay da man". Or is there something we can do that provides some "reasonable" protection for the password without requiring a full HTTPS implementation.

In looking at alternatives, there are some JS based crypto libraries that will do a SSL handshake. But, they need a certificate. Back to that issue. And this particular requirement really doesn't require encryption. the plain text password is not stored on the server, so using a cert and full ssl is overkill. The browser needs to prove that it knows the password without sending the password. Options:
Full SSL
Overkill, costly, complex for many sites. But if site needs it, absolutely the way to go.
Simple encryption
Use something like play-fair encoding, xor with strikeone key, etc. This solves the simple eavesdrop problem but is security through obscurity. Once algorithm is know, password can be readily recovered.
Client sends a simple hash of the password
Subject to replay. Server either can't use salt, or the server needs the plain text password. Both really bad. Hash without salt is very easy to attack with brute force.
Server sends salt, client returns password hash
Still subject to replay. Any single ID always gets the same salt, returns the same hash. Since the server doesn't have the plain text password, it can't vary the salt for a given user.
Server sends salt and nonce
Client returns bCrypt{ Nonce, bCrypt{ salt, password}} No replay - the Nonce changes for each authentication attempt. Nothing easily cracked.
Status Quo
Password sent in clear, easy to eavesdrop, potentially even logged by apache if %q querystring is included in the LogFormat. Why even bother encrypting the .htpasswd file.

For the purposes of many Foswiki sites, I believe this solution is reasonably adequate.

-- GeorgeClark - 05 Jan 2012

From http://code.google.com/p/digestj/wiki/DigestJ:

I'm implementing an auth scheme quite similar to your's. However, there is something bothering me with your technique, you use the digest to auth user, but just after a successfull challenge, you fallback on a classic php cookie session. So after a user has authenticated, you just have to steal his/her session_id - stored in a cookie - to steal his account. So you have to use HTTPS only with secure cookie only to shield your users from such attacks. The original rfc2617's digest-auth, however, does not suffer from this weakness, as every request send to the server contains a challenge that the user have to succeed or a 401 response will be raised.

George's scheme might be vulnerable to this as well ... that an attacker could impersonate a user by getting the session cookie after the authentication is successful. I guess that getting around this flaw would entail a more radical change in Foswiki, but I don't know enough about the internals to say.

-- KipLubliner - 05 Jan 2012

We're protecting the user's password, something simple and stupid that they've probably re-used 12 times on all the other websites they've used.

To protect the session cookie properly you really do need SSL.

But it's extremely valuable to prevent the plaintext transmission of user passwords.

-- PaulHarvey - 05 Jan 2012

Agreed. I updated the proposal. This is really to only stop the practice of passing passwords in the clear.

It needs some more thought if we are to apply it to the change password dialog. That page also passes the old and new password in the clear. The old password could be handled the same way, but the new password needs more thought. This probably has more holes than swiss cheese but... client could use the old password salted hash as the key to encrypt a new salt:hash calculated for the new password. That way the server could decrypt the new hash using the old password hash as the key.

The bottom line - want security, use SSL. But we shouldn't make it quite so easy for password interception when SSL not in use.

-- GeorgeClark - 05 Jan 2012

George, sorry for the implication, I wasn't calling anything you proposed "stupid" - it's just that someone us older hands used to deal with used to believe in "security by obscurity".

What you describe is PKE - the salt is the public key, in this case. The nonce protects the exchange. Good point about the JS only being required where a password has to be entered. It would be nice to build this into a field type, or some aspect of the Func API, to support plugins that need to prompt for passwords to pass on to other services - e.g. external DBs (or other sensitive form data).

I'm happy with the proposal as it stands, so long as it include doc that clearly describes the tradeoffs between SSL/template.

-- CrawfordCurrie - 05 Jan 2012

Edited some of the above to reflect some conversation today on IRC Start of discussion on IRC
  • Added ref to document on why not to use JS for security
  • Added alternatives of a "simple encryption" mechanism like play-fair, xor, etc.
We agreed that whatever we do, we must not market this as truly secure. Only SSL fills that requirement.

Thought of another possible issue. BuildContrib extension upload.

-- GeorgeClark - 06 Jan 2012

Here is another take on the problem: http://cslyon.net/2011/05/10/sha-512-w-per-user-salts-is-not-enough/ however trying to protect things with a secret shared key is not really a good idea. The user comment suggesting bcrypt as an alternative solution seems appropriate.

-- GeorgeClark - 06 Jan 2012

Tasks.Item11518 adds bcrypt encryption to 1.1.5. Any changes to Template authentication will be deferred to 1.2

-- GeorgeClark - 27 Feb 2012

Changing to a Parked proposal, and referenced it in UserAuthMapping2dot0.

-- GeorgeClark - 19 Nov 2015
Topic revision: r18 - 19 Nov 2015, GeorgeClark
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy