Compartilhar via


You Want Salt With That? Part Four: Challenge-Response

My friend Kristen asked me over the weekend when I was going to stop blogging about crypto math and say something funny again. Everyone's a critic!

Patience. my dear. Today, the final entry in my series on salt. Tomorrow, who knows?

***********************

So far we've got a system whereby the server does not actually know what your password is, it just has a salted hash of your password. But we're still sending the password over the wire in clear text, which seems risky.

System #4

What if the

hash

goes over the wire? The client sends the user name to the server. The server sends the password salt to the client. The client appends the password to the password salt, hashes the result, and sends the hash to the server. The server compares the hash from the client to the hash in the user list. Now the password never goes over the wire at all. Awesome!

Unfortunately, this system is worse. In previous systems the eavesdropper got the password; in this system the eavesdropper gets the salted hash. The eavesdropper can then write their own client which sends that username and salted hash to the server.

And the "steal the password list" attack just came back; now an attacker who gets the password list gets all the salted hashes, and can use them to impersonate the user. Sure, the attacker will still find it hard to deduce the original password from the salted hash, but he doesn't need to deduce the password anymore. (Unless of course the attacker is attempting to deduce your password on one system so that he can use it to attack another system that you use. This is why it's a bad idea to use the same password for two different systems.)

Essentially we've turned the salted hash into a "password equivalent". Can we fix this up?

System #5

How about this?

The client sends the username to the server. The server creates a

second

random salt which is NOT stored in the user list. This random salt is usedonly once -- we either make it so big that odds of generating it again are low, or keep a list of previously used random salts and pick a new one if we have a collision. We'll call the random salt "the challenge" for reasons which will become apparent in a minute.

The server sends the user's password salt and the challenge to the client. The client appends the password salt to the password and hashes the salted password. It converts the salted hash to a string, appends the string to the challenge, and hashes the resulting string to form the "response" hash. The response is sent across the wire.

The server then does the same thing – converts the stored salted password hash to a string, appends it to the challenge, and hashes the resulting string. If the response from the client is equal to the value the server just computed, then the client must have computed the same salted hash, and therefore knows the password.

Now what does the eavesdropper know? The eavesdropper knows the username, the password salt, the challenge and the response. The eavesdropper has enough information to launch an offline dictionary attack against that user. But since the random challenge is never going to be used again, the fact that the attacker knows a valid challenge/response pair is essentially irrelevant.

This system has the downside that an attacker who gets the password file has obtained password equivalents, so no dictionary attack is necessary. (Unless of course the attacker is trying to determine a user's password in order to try it against the user's account on a different system!)

Fortunately, these weaknesses can be mitigated somewhat by changing your password frequently, not using the same passwords for different accounts, never using common dictionary words as passwords, and making passwords long -- passphrases are better than passwords.


This general challenge-response pattern is quite common in authentication systems that rely upon shared secrets, becauseat no point is the original secret password ever actually stored or transmitted! With such a system, a machine that does not know your secret can verify that you do know it -- almost seems magical, doesn’t it? Of course, now that you know how it works, it's not quite so magical -- the salted hash is essentially the shared secret, and it is stored and transmitted.

Clearly we could go on, adding more and more layers of encryption to this system to further mitigate these vulnerabilities, but I'm going to stop here. Readers interested in ways to solve problems such as mutual authentication (where the server authenticates the client AND the client verifies that it is talking to the right server) or other heavy-duty authentication tasks should read this charming dialogue on the design of the Kerberos authentication system: https://web.mit.edu/kerberos/www/dialogue.html

The foregoing is of course just a sketch with lots of details glossed over and greatly simplified for didactic purposes. Real professional-grade authentication systems do not work exactly like any of my sketches, though several are quite similar.

Unix boxes used to typically use a 12 bit salt and hash it with a 56 bit password using a DES-based hash, for instance. That's pretty weak! A 12 bit salt only makes construction of a dictionary take 4096 times longer -- one dictionary for each possible salt. In modern systems the more secure MD5 hash is often used now, which supports arbitrarily large salts. Unix boxes also used to keep the user list in the clear, so that anyone could read the salted hashes of the passwords; nowadays the encrypted passwords are kept in a file that can only be read by the operating system itself -- defense in depth!

NT by contrast uses an unsalted 128 bit MD5 hash to store the password equivalent, so it is susceptible to dictionary attacks should the password file be stolen. NTLMv2 is an authentication system based on some of the challenge-response ideas from System #5. It hashes the password, user name, domain, and current time in a fairly complex way; I'll spare you the details. And of course, many NT and unix boxes use Kerberos-based authentication these days.

Comments

  • Anonymous
    February 07, 2005
    An excellent series. I found this very informative.
    Thanks

  • Anonymous
    February 07, 2005
    Fantastic series! Thanks! I would try to come up with working example in PHP to implement this!

    Thanks once again!

    JD

  • Anonymous
    February 07, 2005
    The comment has been removed

  • Anonymous
    February 07, 2005
    (I have a confession: I wrote my own challenge/response, and then a cut-down version of kerebos, for my site. On the other hand, I wrote the first web server it used, several database servers, pseudo-ssl, fat-client web pages, and so on. It was fun, frustrating, and educational. =D)

    Eventually most of it was scrapped for the much more secure and usable Apache/Tomcat + Mysql/Xalan. I'm not smart enough to make uncrackable servers.

  • Anonymous
    February 07, 2005
    Great series Eric.

    Encryption is one area where the more I learn, the less I know...

    Would you believe I once thought XORing data (against a single byte value, no less!) was a great way of encrypting it?

    Thinking back, I'm almost embarrassed to admit it here. Not to mention other equally hilarious forays into encryption.

    Presently, I'm able to use the CryptoAPI in my apps that require it, and leave the implementation of encryption techniques to the mathematicians, thank you.

    Looking forward to more foundation-building articles like the same.

  • Anonymous
    February 07, 2005
    The comment has been removed

  • Anonymous
    February 07, 2005
    System #4, "Unfortunately, this system is worse" : I dont think that sending a hashed password is WORSE than sending the password in the clear! Granted it is not effective, but it put up barriers for the casual snoop who knows how to run a packet sniffer but knows nothing about hashes or writing their own clients!

  • Anonymous
    February 07, 2005
    P.S. Why not just use a timestamp i.e. "2004-06-21T11:29:01.9346744" as the salt, you dont have to worry about unique salts.

    Also, you are still open to man-in-the-middle attacks. The server must sign its hashes using asymmetric encryption otherwise I could pretend to be the server and get passwords from the client whenever I need them.

  • Anonymous
    February 07, 2005
    sorry, I meant use the timestamp as the challenge, not the password salt

  • Anonymous
    February 08, 2005
    You might also want to check out [url=http://srp.stanford.edu]SRP[/url].

  • Anonymous
    February 08, 2005
    Great Series Eric!

  • Anonymous
    February 09, 2005
    Eric, I might have a series of dumb questions: how are password policies enforced? If the server implements them, I assume that the password is sent in clear text? Either way, I suspect the only reason techniques similar to those you describe are employed -- as opposed to full-blown encryption -- is related to available processor resources. Am I right? Do you see this changing?

  • Anonymous
    February 09, 2005
    The comment has been removed

  • Anonymous
    February 10, 2005
    The comment has been removed

  • Anonymous
    February 10, 2005
    If you can establish an SSL connection then all communications have an additional level of encryption, yes. If you're going to do that, then there's no reason why you couldn't just send the password. No need to muck about with challenge-response, etc, to avoid a replay attack. The unique session key is sufficient to avoid the replay, and the session key is encrypted with the server's public key.

    However, SSL does beg the question somewhat. SSL builds a trustworthy connection over an untrustworthy network by leveraging a trusted root certificate. Essentially for any SSL-based scheme to work, every client must trust a given root cert.

    Essentially we're talking about mutual authentication here, which is quite a bit harder than one-way authentication. I might do another series on mutual authentication some time.

  • Anonymous
    February 11, 2005
    The comment has been removed

  • Anonymous
    February 11, 2005
    Sorry for replying bit late.

    Eric, when I said that I will try to create working example in PHP, I didn't mean that I will write my own encryption system. What I meant was I would write a working login page which will use MD5 or SHA on both client side (using Javascript) and on Server side (using PHP) to authenticate a user. This example can teach others how they can go about implementing the same in their projects.

    I hope I am clear this time.
    JD

  • Anonymous
    February 13, 2005
    "What I meant was I would write a working login page which will use MD5 or SHA on both client side (using Javascript) "

    But why? IMO, hashing is not encryption. And we showed that it is pretty easy to dictionary attack. Using a true encrypted session such as SSL or WS-SecureConversation would be more useful and secure.
    --William

  • Anonymous
    September 06, 2007
    A recent question I got about the .NET CLR's hashing algorithm for strings is apropos of our discussion

  • Anonymous
    January 11, 2008
    -> "IMO, hashing is not encryption..." It is a form of encrpytion, plain and simple. -> "we showed that it is pretty easy to dictionary attack" No more or less so than symmetric encryption is. -> "Using a true encrypted session such as [SSL] would be more useful and secure..." Do you thin kthat SSL doesn't use hashing to authenticate messages?  Think again.