27

For instance, lets look at a common login system for a website

  1. HTTPS connection is made
  2. User submits credentials via POST
  3. Server-side code hashes the password and looks if it matches the user name
  4. Session is initialized, and a key may be issued to log in again without passwords ("remember me")

This is generally the status quo right now, where passwords are all computed on the server. But, if we do the following client-side it's considered a bad practice:

  1. HTTPS connection is made
  2. JavaScript calculates the hash (may request specific user salt)
  3. Script sends AJAX with the user/hash values
  4. Server-side code looks if the password's hash and user name match.
  5. Session is initialized, and a key may be issued to log in again without passwords ("remember me")

Can someone explain why this other method is considered bad practice to do it CS instead of SS? Both transmit over SSL, both create the secure hash, and both authenticate should be be considered reliable with well-written code. Both are susceptible to XSS and other bad design.

4
  • 5
    This may help stackoverflow.com/questions/1380168/…
    – zedman9991
    May 18, 2011 at 14:20
  • 2
    I always assumed that at least part of the reason was accessibly. Your second example relies on javascript, and would not work for clients without js support. However, i'm interested to see why this would be a bad idea from a security standpoint. Over HTTP at least, I would assume this would at least somewhat increase the "security", since compromising the session does not immediately yield the user's password (which they likely use for other sites).
    – crazy2be
    May 18, 2011 at 22:05
  • I'll let the answers address the general question, but to make your two proposals even remotely similar (i.e. someone at the server doesn't get to see the client's authentication token), then Server side step 3 needs to ALSO be client side step 4. Further, "hashes" is incorrect at step SS3, the new CS4, and very likely at CS2 as well; applies a standard password hashing function is what is required - BCrypt, SCrypt, or PBKDF2 with as large a work factor/iteration count as possible under peak load. Additionally, the salt in all cases must be random and long (8-16 bytes). Mar 27, 2014 at 2:03
  • This would have the same effect as storing passwords in plaintext.
    – Cano64
    Jun 23, 2019 at 1:11

9 Answers 9

38

What you've described isn't improving the security of the system. Its not a matter of opinion or emotion, security just doesn't work that way. In your example the hash(salt+password) is now your password. If it wasn't over https, then an attacker could just replay that value. Also you didn't really address owasp a9 aka "firesheep" style attacks.

10
  • 3
    OTOH, if you hashed (salt+password+nonce), then it becomes a one time password (no replay); that, however, is kind of pointless over HTTPS (not to mention that the browser already implements that as HTTP Digest Auth). May 18, 2011 at 19:42
  • 1
    @Piskvor actually the SMB protocol did something very similar. A CVE was issued because the nonce would repeat after a couple thousand tries. If you sniffed the wire looking for the Nonce, then you can keep trying to replay the login until the server reuses the nonce.
    – rook
    May 18, 2011 at 19:55
  • 5
    well, if you reuse it, then it's hardly a nonce anymore, is it? ;o) Just goes to show that Security Is Hard. May 18, 2011 at 20:13
  • +1 for good answer and showing me owasp. Could be useful for explaining threats to less technical people. May 20, 2011 at 9:48
  • 1
    @Pacerier I am a penetration tester, I study how software is built and how it fails.
    – rook
    Apr 6, 2015 at 23:36
16

It has to do with the general scope of what you are trying to protect. If you are developing a server-side application, you are trying to protect the server from both the user and his client system. Having the user's system (ie, the client) do your security work for you doesn't really help the server stay protected. There's usually an assumption that when the client is doing something, even it if it working at the behest of the server (in terms of JavaScript delivered by the server) that the client is innately untrusted, because a hacker can take control of the client side app and submit input separate from the JavaScript.

A server hashes password to inhibit issues resulting from the disclosure of the password store. If an attacker gets a hold of the password hashes, it should be impossible to make use of them by sending the passwords from a hacked client machine - because the server is doing its own hash. If the server delegates this work to the client, then the server is also delegating a security function.

This all depends on how you define your boundary of protection. If you have reason to believe that there is absolutely no threat from the client machine and that not client system could ever be hacked, then this issue goes away... but I don't know of any web system that can assuredly make this claim.

3
  • 1
    You could have two layers of hashing to mitigate against the problem you describe in the 2nd paragraph, as HTTP Digest does (with a nonce on the 2nd hash). The first HA1 is stored on the server and the client knows how to compute it (when the user enters the password), and the client is also able to compute HA2. Hashing on the client side isn't really an issue (of course, the server has to verify what the client sends against the hash it computes itself).
    – Bruno
    May 18, 2011 at 16:38
  • I'd also say that a server hashes passwords firstly to protect the user's passwords (and only as a consequence, protect itself, because good client protection benefits the whole system). If a password stored has been leaked, it's usually a sign of a problem on the server first. Computing the hash to verify on the client or on the server doesn't really make a difference here (provided the server verifies the result against what it trusts: its own hashed password store, of course).
    – Bruno
    May 18, 2011 at 17:00
  • 1
    @Bruno - First comment - I'm afraid I don't get what you're driving at here... there's a whole lot of hashing in what you propose and I don't see the value of it - if the server has to recompute what the client does based non a pre-hashed piece of data, why have the client make the hash at all?; second comment - I agree, but I'd skipped this since the point of the question seemed to be - why not delegate things like hashing to the client. May 19, 2011 at 14:00
14

The core reason is not hate... it's insecurity. A general principle is to trust nothing you don't have control of. In the case of a user authenticating to an application, unless you provided the laptop to the user, configured its controls, and those of the environment it sits in, you can't trust it.

The traditional way to look at it is that if an attacker has physical access to a computer they can do whatever they like.

With a simple browser that just sets up an encrypted link there is very little that can be compromised, with a thicker client any functionality client side could be compromised.

0
10

If you're calculating the hash on the client side, you add the risk that an attacker only needs a hash of a user's password to log in instead of the actual password. You could probably get around that by sending a nonce to hash the password with (basically a per-session salt), but then you'd still have to hash that on the server side too, so why bother doing it on the client at all?

8

I'm not sure if people "hate" the mechanisms you describe on the client side.

I think this has more to do with legacy practices and uneven support on the client.

Firstly, you need JavaScript for implementing this, if you want to do it via form-based authentication. HTTP Digest provides something similar natively, and is supported by most browsers (including those without JavaScript support), but it doesn't "look good" and cannot be integrated very well with the general branding of the website. To be fair, having some identifiable branding where you're going to enter your password can be a good thing from a security point of view too. In addition, one of the major shortcomings of HTTP Digest implementations in browsers is that the popup boxes don't look much different from those that use HTTP Basic, which doesn't offer any protection.

Secondly, as far as I'm aware, JavaScript cryptographic functions support vary from one browser to another. While it can sometimes be tolerable to have inadequate support for certain features across browsers, being able to log in is quite fundamental and that's something you never want to be broken.

Finally, you still have to trust the code sent by the server, so there's no benefit here if you're logging in via HTTPS anyway. When you send your password over HTTPS, you're effectively giving it to the server and trusting the server to handle it correctly. If you don't trust the server enough to give it your password, there's no point trusting it with the JavaScript it sends you to perform the authentication.

2
  • 2
    +1 for "if you don't trust the server". Also if the server knows the hash and you can just log in with the hash, then the hash becomes the password effectively. May 18, 2011 at 14:23
  • @StephenPaulger, about hash/password, yes indeed. Unless you're using SSO or client-certificate authentication, authentication is biased towards having the server calling the shots on what the password ought to be (pushed to the extreme, it could even reset your own password and lock you out of its services). In that context, "ownership" of the password is shared between the user and the server anyway.
    – Bruno
    May 18, 2011 at 14:35
6

For your specific example there is at least one business reason and one security reason that it might not be done that way.

Business reason

  • User requires Javascript. This is not necessarily always enabled.

Security reason

  • Passwords are normally stored hashed with a salt. You would need either a constant salt, which would make it less useful; share the specific user's salt, which adds complication and again makes it less useful (though not to the degree of having a constant salt); or perhaps a username based salt.

Not all systems do avoid using a system like you describe, Lastpass calculates hashes on the client-side (described in a Security Now podcast (transcript).

1
  • In terms of storage, you could store the equivalent of the "HA1" hash in HTTP Digest (that's more or less using a username salt indeed).
    – Bruno
    May 18, 2011 at 14:19
3

Doing any security on the client end has one huge problem: you assume the client is going to behave well. What if they don't? So you let them calculate a hash...now you got people using pre-calculated hash tables banging on your server at super-fast rates, taking out one of the major benefits of hashing: their slowness. If you don't do that, but have the server do the hashing for the client, then you can have the client request zillions of strings to hash, slowing down the server... So basically it's 'pick your poison.' You have to pick the answer based on domain-specific knowledge: do you trust your users? Is it internet facing? If it is, then you probably don't want to trust the users.

Learning about protocol analysis is probably the best way to understand why you don't want to let clients do much security-related. Replay attacks, precomputing attacks, they all happen because you give clients too much freedom, too much choice. Read about Needham-Schroeder-Lowe protocol, how it came about, how they discovered an attack, and what's the fix, it's a great demonstration of how you want to build a protocol that is safe from malicious clients.

1

To answer your question it suffices to understand the point of hashing passwords.

The reason passwords are hashed is to store a derivative instead of the actual password. This prevents mallory from logging into the server as alice even if he would have compromised the database of the server (a quite common problem). You always hear about credit card numbers being disclosed and not passwords, because the hashed passwords are not vulnerable in this way.

This also prevents mallory from discovering a password that alice might be using on other websites/software.

It might be noted, although not necessary to answer your question that a salt is used on servers when hashing. This is done to prevent rainbow table attacks, because it is possible to iterate random passwords and hash them with commonly used hashing algorithms to store them in a table. Mallory could now retrieve the passwords again by doing a table lookup. This is common technique for recovering operating system passwords. (at least windows does/did not use a salt). The essence of a salt is that it is unique per installation/website as well as secret (it will usually be stored in php/config files rather than in the database with the rest of the passwords).

On client side security. What you propose is not really client side security, as you just propose to omit a step from the procedure of storing passwords. Note that every user is free to use a hash as a password.

The point to understand is that you should always do security on the receiving end of some connection (especially but not only on a publicly accessible service). Since a web server can be connected to by anyone you cannot trust anything that comes in. Any security that might have happened on a client has to be done over, because some attacker might easily bypass that security.

As a counter example, note that the client also has security. When the browser is the receiving end, it will restrict access to the host file system for example to scripts on websites.

1

Sorry to be late to the party...

The answers are all right that by using the received hash as-is to compare against your database, you have made the hash the new "password". However, if you add a random challenge (aka "nonce") the hash transmitted can no longer be used as the new password.

I strongly disagree with those answers that generalize that client-side security is a bad thing. We are using client-side hashing (using a salt and a nonce) on a high-profile website for years. Mainly for two reasons:

  1. Avoid plaintext passwords from accidentally ending up in server logs
  2. Prevent "wandering eyes" of system administrators from learning passwords

Using client-side hashing we will never be exposed to the users' plaintext password, and have thus reduce our liability.

We are currently trying to engineer key stretching into the system, which proves to be a bit harder. See Challenging challenge: client-side password hashing and server-side password verification

1
  • Can you describe the protocol used?
    – curiousguy
    Jul 12, 2012 at 21:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .