Our application has recently gone through penetration testing. The test found one critical security breach, which is essentially:
The problem:

Attacker sets up a WiFi spot.
User enters our site (which is HTTPS).
Using a tool like Cain, the attacker either redirects the user to HTTP, or keeps them in HTTPS with a spoofed certificate. (Either way, the user had to go through the "get me out of here"/"add exception" page)
User enters her user name and password.
The password is posted to the MITM attacker, who can see it. Apparently Cain has a feature to automatically harvest user name and passwords, and our password would be easily caught there.

Suggested solution
The report recommends encrypting or hashing the password in the client side (using JavaScript), in a way that cannot be replayed (e.g. using one-time pad / time stamp). They could not recommend a specific schema (they did mention client certificates, which might be unpractical for a large application).
Is is worthwhile encrypting or hashing the password before posting it? It is common practice?

This recommendation makes no sense:
The JavaScript code used to hash or encrypt the password has to be transferred to the client too. If the attacker is able to mount a man-in-the-middle attack he will be able to inspect the JavaScript code used for encryption too or might even replace it with something else (like no encryption).

Hashing instead of encryption makes even less sense, because this would only work if the server accepts the hashed password instead of the real password. In this case the attacker would not even need to know the password, he only needs to know the hash.

What might help would be client certificates because you cannot mount an SSL man-in-the-middle attack which preserves the client certificate (and a downgrade to plain HTTP would not send the certificate either). But, because you have to distribute the certificates to the clients first and make them install it inside the browser, this solution works only when you have few clients.

Apart from that: if the attacker is in the middle he might not need the password at all. All he needs is that the victim has logged in and then the attacker can take over the existing session.

It might also be useful to detect such a man-in-the-middle situation, so that you can inform the user and deny login from compromised networks. Some ideas to detect connection downgrades (that is http to attacker which than forwards it as https):


Check the method of the current location with JavaScript.
Create a secure cookie with JavaScript. It should only be send back if the site is served with https (that is no downgrade).
Include a script as HTTP which servers an image and check at the server side how the image was included. If it was included as HTTPS you can assume a HTTP downgrade attack because you've explicitly included it with HTTP. If it gets accessed with HTTP you have a downgrade attack too (but with a smarter attacker) or the browser does not care about mixed content.


And on how to detect man-in-the-middle with faked certificates:


Setup a second https site (with a different hostname) and construct an ajax request to this site in a way, which is not simple for the attacker to change to http (e.g. create URL dynamically). If the attacker just tries to MITM any site this ajax request will fail at least with some browsers, because the certificate is not trusted and the browser will only prompt the user for the primary certificates of a site.


Of course all of this only helps against an attacker which is not really determined to hack especially you, but just takes the easiest targets. In this case all you need to have to do is to be a bit harder to attack than the rest.
