I've read a lot here and other places about using a cookie for a "remember me" option, but what I'm looking for is a way to design a cookie to record success of a two-factor authentication. This is what, for example, Google does: If the second step succeeds (e.g., you entered the code that you received via SMS), then it sets a cookie good for a period of time (e.g., 30 days) that means that the second step can be bypassed. Call this the "verification cookie." My understanding is that if in that time you logout and then in again, it won't do the second step, but only the first step. (I tested this and that seemed to be the case.)
My question is how to design this cookie. One idea is to put the user ID and a 128-bit random number in the cookie, and then to store that number in the database along with the user ID. This is what Charles Miller recommends (http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/) for persistent-login cookies.
However, that's not good enough, I think. The problem is that, since the user is using a two-factor authorization, whatever cookie is used to record that the second step was successful, should be safer than would be the case with a one-factor authorization.
What I want to avoid is this: The cracker has the hashed/salted passwords from the database, and has somehow gotten the password. If he/she has that much, I assume that the 128-bit random number that was in the verification cookie is available as well. (If the cracker has gotten the password some other way, and doesn't have the database, then the verification cookie is safe unless he/she has physical access to the computer. I'm only worried about the compromised database case.)
Maybe an idea is to encrypt the 128-bit random number? (Needs to be 2-way -- not a hash.) The encryption key would be accessible to the application, maybe stored however the database credentials are.
Has anyone implemented what I'm calling a verification cookie (not a persistent login cookie) and can tell me (us) how it was done?
UPDATE: Thinking about this, what would I think be secure enough would be this: Cookie consists of userID and 128-bit random number -- call it R.
Database contains password and R, each hashed and salted (e.g., using PhPass). R is then considered to be a second password. Benefit: Even if first password is bad (e.g., "password1"), R is a very good password. Database really can't be cracked, so it should not be a worry. (I was unnecessarily worried about it, I think.)
I think you have a pretty good plan here. Generally speaking the cookie should be completely random and should not contain any data that is used by the server. The idea is that anything that is client controlled can be tampered with. Even when the value is encrypted, I've seen attackers twiddle bits and get the tampered data to decrypt to a different user's ID (yeah that one scared me a bit). That being said I think Charlie Miller's suggestion is fine, because 128-bits is a good amount of entropy. Me personally, I would go with completely random bytes for a cookie such that no pattern emerges whatsoever.
Our last implementation of a verification cookie was a completely random 256 bit value printed in ascii-hex that was mapped to a user ID and session information in our DB. We kept the session information encrypted with a secret key, so if an attacker SQL injected our DB it would all be useless encrypted info. Of course a total compromise of the DB machine would provide access to the key, but that is a lot harder to do because it involves multiple exploits and pivots.
Some good advice is not to over-think it too much. We ran into implementation problems because we "over-engineered", and in the end we didn't get much security advantage anyway. A simple random number is the best you can do (as long as it is long enough to provide sufficient entropy).