Recall that current forms of two-factor authentication (2FA) rely on:
Something you know, i.e. your password
Something you have, i.e. your one-time password (OTP) generator on a trusted device
The procedure is as follows:
- Client sends
username
(public) - Client sends
password
(private) - Client sends
otp = HashOTP(client_seed, time())
- Server looks up
salt
,salted_password_hash
, andseed
forusername
- Server compares
HashPW(password, salt)
againstsalted_password_hash
- Server compares
HashOTP(seed, time())
againstotp
- Client is authenticated iff all comparisons succeed.
The idea, of course, is that even if all the transmitted information is intercepted by an attacker, the attacker is unable to authenticate because he does not have the client_seed
to generate a subsequently valid otp
.
So far so good, except that interception of password
permanently reduces 2FA to 1FA.
"Permanently" because the user will have no idea his password
is compromised, and will therefore have no reason to change it.
So, ironically, not only is otp
a "one-time password", but so is password
itself. (!)
(Now, at this point, I'm sure some of you are ready to close the question, thinking, "But TLS should have protected the password during transit, and if the client itself is compromised, then the problem is invalid!" If you're in that camp, well, realize that's not how the world works. A passive keylogger on the client could be secretly recording everything but the actual stolen credentials are likely to stay unused until a while after, when the user is unlikely to notice it. So it makes sense to render any such keylogger useless if possible.)
Given that the user will reuse password
without even knowing that his 2FA is really 1FA, a theft of his device is all that stands between him and his account, and he is likely to think he is safe because he has no idea his password might have been compromised.
How, then, can we address this?
Initially I was thinking that the user could type his password
into his trusted OTP generator and generate a single one-time password that will authenticate him without ever having to send his actual password.
But now it seems to me the server would need to know the password itself in order for this approach to work. In other words, it defeats the storing of hashed passwords—i.e., a server-side leak will now reduce all users' 2FA to 1FA, instead of a client-side leak.
Basically, it almost seems that 2FA is fundamentally 'single-use': any attacker with read-only access will reduce 2FA to 1FA, and the client and server can remain indefinitely unaware.
But I don't know if this is really the case.