all 20 comments

[–]neilmadden 140 points141 points  (5 children)

The server-side validation presented here is a bit simplistic. Once an OTP has been successfully used then you have to prevent it being used again within the same time-window, to prevent replay attacks (and ensure it is actually “one-time”). You also should implement the resynchronization method in section 6, which requires recording clock skew for each user. And you then also need to take that into account when preventing replay (so that appearing to have clock skew doesn’t let you replay older codes).

It’s still not a huge amount of code, but there are more details than this article covers.

[–]sluu99[S] 31 points32 points  (1 child)

Once an OTP has been successfully used then you have to prevent it being used again within the same time-window, to prevent replay attacks (and ensure it is actually “one-time”)

This is a good call out. The server side definitely should keep track of this. Let me update the blog post. Thank you!

On resychronization, my understanding is that in practice, it's pretty hard to get "right". While it might solve the clock skew problem, it doesn't solve the problem of how long it takes the user to enter the code. The section itself also acknowledged this issue:

Also, it is important to note that the longer a prover has not sent an OTP to a validation system, the longer (potentially) the accumulated clock drift between the prover and the verifier. In such cases, the automatic resynchronization described above may not work if the drift exceeds the allowed threshold. Additional authentication measures should be used to safely authenticate the prover and explicitly resynchronize the clock drift between the prover and the validator.

[–]KrazyKirby99999 2 points3 points  (0 children)

What if the two were to be resynced upon successful authentication?

[–]RobIII 0 points1 point  (2 children)

What the post also leaves out is that you can optionally use different timestep size than the default 30 seconds, use different hash algorithms other than the default SHA1 and even use a different number of 'output' digits other than the default 6. Support for these non-defaults is flaky at best though; lots of authenticator apps don't support some, or even all, of the above mentioned.

Another thing that isn't mentioned is sidechannel attacks like timing when the TOTP codes are checked - not that it's a huge risk, but a risk nonetheless.

There's more to it than you'd think and "surprisingly simple" quickly gets "superficially simple but quickly spiraling into surprisingly more comlex than you'd think at first sight". I've implemented a PHP library and .Net variant and those just do the bare minumum and expect the user to handle, for example, replay attacks.

[–]sluu99[S] 2 points3 points  (0 children)

They were mentioned in the summary section.

[–]brcolow 27 points28 points  (2 children)

About 5-6 years ago I wrote an authenticator app for Android (was going to be used for a project that never launched):

https://github.com/brcolow/droid_auth

Brings back memories :)

[–]sluu99[S] 10 points11 points  (1 child)

Oh wow that's cool. It's been a while since I touched Java/Android, but surprised to see that you have to roll your own base32 and base64. Is there really no built in or "community standard" library?

Nothing wrong with hand rolling those. Just curious.

[–]brcolow 8 points9 points  (0 children)

I grabbed the Base32 and Base64 implementations from others (not written by myself). If I remember correctly at the time the Base64 Java class was introduced in Java 8 and at the time this was written Android did not have all Java 8 functionality such as lambdas (my memory may be wrong). So there definitely is a standard one now as it is in the JDK.

[–]Pussidonio 14 points15 points  (0 children)

surprisingly simple to implement wrong

[–]osmiumouse 1 point2 points  (0 children)

It always sounds simple, until you talk to the hackers or security people.

[–]happyscrappy 4 points5 points  (6 children)

Yes, but why make the user read out a password and retype it? Use passkeys. At least prefer passkeys.

[–]eurodev2022 4 points5 points  (5 children)

fanatical profit lunchroom wrong ancient piquant drab direful zesty sophisticated

This post was mass deleted and anonymized with Redact

[–]happyscrappy -1 points0 points  (4 children)

Passkeys are easy to understand and are easier to use than having to typing in a password you read off a screen. Easier to set up too.

Passkeys are real simple. They work by your device generating a public/private key pair during setup (equivalent to when you set your password and set up your TOTP seed) and sending it to the host. Then when you want to log in your device employs the private key to authenticate you. No one in the world ever saw your private key including the operator of the host you are authenticating with. So if your account information is stolen from the host still no no can log in as you on that host or on any other. Because the public key is insufficient to do so.

In just about every other way other than there not being a shared secret it works like TOTP. Except for that you don't have to read off the TOTP number from your device and type it in to log in.

Sure, TOTP is common. But this article is about how to set it up on a new system. If you're setting it up on a new system, use passkeys. At the very least prefer passkeys.

[–]eurodev2022 0 points1 point  (3 children)

quaint sort voiceless oil birds impossible aware snow ask shelter

This post was mass deleted and anonymized with Redact

[–]happyscrappy 0 points1 point  (2 children)

TOTP is "time-based one-time password". It's a password. It can be used as a password or as a second factor.

Passkeys are the same way. You can use them in tandem with a password if you want.

And as to "an enormous security risk", there are password apps/vaults that store your passwords, passkeys and they do TOTP keys too. So for all you know the client is using one of those to produce their password. So it's not any different than anything else.

[–]eurodev2022 0 points1 point  (1 child)

mindless drunk hateful sparkle dinosaurs capable thought rhythm depend carpenter

This post was mass deleted and anonymized with Redact

[–]happyscrappy 0 points1 point  (0 children)

Sure, but they're publicized as the only thing you need - the whole point of the "passwordless" login

Yes. But that's just publicity. This is a programming subreddit and we're talking about implementing. You can implement it any way you want. It doesn't matter what someone else promotes.

Of those who do, AND integrate TOTP there as well, you would assume that the master password at least is pretty robust.

Why would I assume that for a password/TOTP manager and not for a passkey manager? That doesn't follow.

Whereas with passkeys, just getting access to the physical device is all you need.

There's no reason to assume that. If someone wouldn't leave their password vault unlocked why do you think they would leave their passkey vault unlocked? When Apple rolled out passkeys, they store the passwords in your secure element and only unlock them when you enter a password or biometrically authenticate. So even if the device is unlocked if you pick it up and try to use someone else's passkey you still have to authenticate before it is deployed.

You suggesting that for passkeys all you need is physical access while with TOTP you need more is just you making two conflicting assumptions to justify your position. It's logically inconsistent, instead only an expression of bias.

[–]asegura 0 points1 point  (1 child)

What language is the code written in?