In a web application that (as a side effect of implementing the password anti-pattern) stores passwords, one recommendation is to bcrypt to store them (or computationally expensive one-way salted hashes of them). However, doing this makes the server process spend most of its time doing CPU intensive bcrypt comparisons, since the whole point of using bcrypt is that it takes time to test a password.

My question is this: is it safe to cache (in-memory) the response of a bcrypt comparison? I'm not worried about spending the time once per process, but once for every single request will become a scalability bottleneck. Bonus points (?) if you can shed some light if it's common practice to do so.

Things I've considered:

  • If it's in-memory only then it's harder to attack
  • The persistent store is as secure as before if it's compromised
  • Lowering the bcrypt load factor makes the password database more vulnerable, if compromised

Password hashing is used for long-term password storage to deal with the case of an unwanted read-only database access by the attacker. This is a realistic threat; such a read access is a common outcome of successful SQL injection attacks.

On the other hand, a common model is to assume that the attacker cannot read the RAM contents of a live server. In that model, there is no problem in caching the password themselves in RAM, which avoids having to touch the database at all: password verification is just an in-memory comparison. Scenarios where an attacker can read the RAM without being able to take full control of the machine are quite convoluted and unlikely to apply.

Caching the bcrypt output does not make much sense by itself, because, for this information to be useful, it must be somehow indexed by the password and the salt (internally, there would be a map "password+salt" to "bcrypt output"), so the attacker who can read the bcrypt output from the RAM should be able to read the cleartext password itself. Still, in a given existing server implementation, it may be easier to retrofit a RAM cache as such a map (in the code, you can plug that in the "bcrypt engine" transparently); as explained above, caching bcrypt output is no worse than storing plaintext passwords in RAM, which is usually fine.

  • Thanks for the insight. It's nice to know that it's common to assume that it is not a typical attack vector to go through the contents of RAM. Wrt storing the passwords in a simple dictionary, would it help to hash them first, e.g. salting with the user id? or maybe even source IP address? I wouldn't have to cache the user id. I believe it would make a successful attack of a running server less effective. – mogsie Jan 9 '12 at 22:31
  • 2
    @mogsie: If you're going to take that route, then yes, I'd feel more comfortable if the passwords were hashed somehow even in the RAM cache. Assuming that you want to use the same hash value also as the cache key, I'd suggest a mapping from, say, HMAC(K, password || user_id) to the bcrypt output, where || means concatenation and K is a random MAC key chosen when the RAM cache starts up (e.g. by reading 16 bytes from /dev/urandom). – Ilmari Karonen Jan 10 '12 at 11:09

From your question, it sounds like you're sending the password to the server on every request. If so, I have to ask: why on earth would you do that?

The usual pattern is to send the password once on a "log-in" request. If the password is correct, the server sends the client a randomly generated temporary authentication token. This token should be long enough to withstand a brute force attack even without key stretching techniques, and have a short enough validity period that compromising it presents only a limited risk. (For particularly security-critical operations, such as changing the long-term password, it's reasonable to ask the client to resend the original password.)

Typically such tokens are stored on the server side in plain in some short-term session storage. I suppose you could hash it with some non-stretched hash function, just so that a compromise of the token storage doesn't automatically compromise all active sessions.

On the client side, it's common for web apps to store the token in an HTTP cookie, so that it's automatically sent to the server on every request. Note that, if you do this, you should take care to protect your app against Cross-Site Request Forgery attacks.

  • 1
    I wasn't asking for a lecture in web security :-) Sending the password on every request is what browsers do, when you use the built-ins like basic/digest authentication. It's the stateless way to authenticate. – mogsie Jan 9 '12 at 21:43
  • 3
    In my experience, neither basic nor digest authentication are in wide use, because the user interface is so restricted and unfriendly, and because it either sends passwords in the clear over and over (basic without TLS), or requires passwords in the clear on the server (digest). If you're using TLS, you've got some state, and I don't know if anyone has some clever way to leverage that state for authentication without re-validating passwords delivered via basic authn. So having state for authenticated access is nearly universal in practice, I think. – nealmcb Jan 9 '12 at 23:47
  • @mogsie: Indeed, I wouldn't go so far as saying that HTTP basic auth is useless, just almost so. I feel less qualified to speak about digest auth, since I've never used it or even seen it in use, but I suppose that in itself says something. In any case, there's a fundamental conflict with "stateless" authentication and key stretching (PBKDF2, bcrypt, scrypt etc.): the whole basis of key stretching is that authentication ought to be an expensive operation, whereas with stateless authetication, you need to re-authenticate on every request, some of which presumably should not be expensive. – Ilmari Karonen Jan 10 '12 at 11:17
  • No this isn't common for password hashes
  • It's uncommon because what you really want to do is assign a temporary session ID after a login rather than continually authenticating the password. This also assists in preventing CSRF attacks if implemented in a certain way.
  • If fixing your architecture takes a lot of work, you can as a stop-gap cache the password <-> bcrypt result for a period of time. Make certain it is stored in memory only and expires after a short period of time. 10 minutes is probably appropriate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.