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


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


random salt which is NOT stored in the user list.  This random salt is used only 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, because at 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:

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 (21)

  1. Sean says:

    An excellent series. I found this very informative.


  2. JD says:

    Fantastic series! Thanks! I would try to come up with working example in PHP to implement this!

    Thanks once again!


  3. Eric Lippert says:



    This series of articles gives just a quick overview of some of the simpler techniques used by authentication systems and the people who try to crack them. There is nowhere NEAR enough information in here to actually build a secure system.

    Let me give you an example. The Kerberos authentication system is breakable if the cryptographic algorithm used allows for mutagenic encryption.

    Muta-what? If you don’t know what that means, if you can’t look at an encryption algorithm and see whether it’s susceptible to mutation attacks, then you can’t write a secure implementation.

    Writing a good authentication system is HARD, it is one of the hardest tasks that face computer scientists today. Realtime robot controllers are a piece of cake compared to authentication systems, so PLEASE leave it to the experts.

    (And I am no expert — I have nowhere near enough experience to write an authentication system.)

  4. foxyshadis says:

    (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.

  5. Phylyp says:

    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.

  6. Eric Lippert says:

    I don’t know if Mike came up with the term or if it’s an old joke, but Michael Howard refers to algorithms like the XOR encryption as "encraption".

    Ah, those witty Kiwis.

    Anyhow, thanks for the compliment — like I said, I’m not sure what I’ll blog about next. I’ve got a whole lot of ideas but not very much time these days.

  7. G. Man says:

    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!

  8. G. Man says:

    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.

  9. G. Man says:

    sorry, I meant use the timestamp as the challenge, not the password salt

  10. Skywing says:

    You might also want to check out [url=]SRP[/url].

  11. Lance Fisher says:

    Great Series Eric!

  12. James says:

    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?

  13. Eric Lippert says:

    You’re now asking about an entirely different problem — how to securely transmit the original secret to the server.

    > I assume that the password is sent in clear text?

    I should hope not!

    > how are password policies enforced?

    Like, say, passwords must be longer than a certain number of characters? Clearly the server must get the password somehow!

    > I suspect the only reason techniques similar to those you describe are employed — as opposed to full-blown encryption

    By "full blown encryption" I assume you mean "using encryption to transmit a message which can be turned back into plaintext by the recipient".

    > is related to available processor resources

    Encryption of short messages such as passwords is cheap, so no, that’s not it. Sending the bits over the wire a reasonable distance is almost certainly more expensive in terms of raw time than doing the math.

    Let me give the shortest reason for why you wouldn’t use "full blown encryption": because full blown encryption is HARD TO IMPLEMENT CORRECTLY, and NOT NECESSARY TO SOLVE THE PROBLEM.

    When it comes to authentication schemes, you want it to be as simple as possible, but no simpler. The key management problems inherent in full-blown encryption — as in SSL, for example — are non-trivial. If you can build an authentication system that doesn’t need them in order to succeed, then do so!

    Now, you are entirely correct in noting that I have hand-waved away the problem of getting the password or pasword-equivalent-hash onto the server in the first place. That DOES require some kind of encryption whereby the message can be recovered by the server. I might do a series on how that works sometime.

  14. William says:

    Thanks Eric. You pointed it out, but I think it is worth saying again that this is not secure nor is any Non-Encrypted means because you can do a dictionary attack as you have all the information you need off the wire. Once you find the password (taking as much time as you need offline), you now have access to this box or any others that use same password. So this is more or less Security-Via-Obscurity which we know is not really security. Even WS-Security and the SendHashed option uses a similar method of using a "Nonce" (one time value), Date "Created" value and "Password Digest" to form a validator that the server can verify. The problem is, all that fancy mojo still does not stop a simple dictionary attack as I point out in "Crack your WSE SendHashed Passwords. "“>!1pnsZpX0fPvDxLKC6rAAhLsQ!178.entry

    And that spec was created by industry experts in the field! Even passwords people think are strong like "Letmein;12" can be cracked in ~minutes. BTW – How many people when forced to add digits to a password start at 1 or 12 and increment them when prompted for a change?

    IMO, I would not recommend any scheme based on hashes unless the session is *encrypted with SSL or WS-SecureConversation (or other). If the session is encrypted you probably don’t need hashes to begin with unless your doing something special with them at the back end. Also, I don’t know why people want to use another database when we have AD/SAM and can leverage its’ tools. So use encryption, get the clear password on server component, and use win32’s LogonUser to authenticate. The ~downside is you have the clear password in a server process for some short time, but only the private key can decrypt, so we can control/guard the logic that uses it closely. Naturally, the Admins can use dictionary attack on ADs password DB, put if we can’t trust the admins, then not sure what solution you could implement.

    So I would use WSE and SecurityContextTokens(SCT) to sign and encrypt data/body. And don’t use UsernameTokens or pw hashes at all. You can get a SCT with public WSE apis. I have also written a way to get a SCT over Soap.tcp and authenticate at same time using PKI encryption and one request/reply pair. Anyone please feel free to review and provide feedback:“>!1pnsZpX0fPvDxLKC6rAAhLsQ!303.entry


  15. Eric Lippert says:

    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.

  16. Dave Anderson says:

    "…refers to algorithms like the XOR encryption as "encraption"…"

    And yet XOR is at the very heart of DES and other algorithms. Heck – we can achieve perfect secrecy with XOR and a one-time pad (assuming a pad made with a perfectly random generator, of course). And it would be a hell of a lot faster than exponentiation over an enormous finite field.

    Encraption may be a funny word, but it certainly understates the power of XOR.

  17. JD says:

    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.


  18. William says:

    "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.


  19. A recent question I got about the .NET CLR’s hashing algorithm for strings is apropos of our discussion

  20. -> "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.

Skip to main content