5

Background

We're starting a project to add an additional authentication factor to our web application. This involves the creation of a TOTP (Time-Based One-Time Password) token that conforms to RFC 6238 (https://www.rfc-editor.org/rfc/rfc6238). This token will be delivered to the user via SMS or email.

I'm aware this approach is not ideal from a security standpoint (eg. Eurograbber, Sandroid, Emmental). I'm hoping to steer our organization to evaluate other options, but in the meantime I'm researching best practices for this approach.

Overview

  • a user authenticates to our web application using their username and password
  • user initiates some action which triggers a risk assessment
  • if risky:
    • pull user's encrypted secret key from database (or generate new random key if it's not there)
    • use secret key and current timestamp to generate TOTP
    • TOTP is delivered to user's cell number via SMS (or delivered via email)
    • Display TOTP input screen
  • User receives and inputs TOTP
    • Pull secret key from database and generate TOTP(s)
    • validate provided TOTP against generated TOTP
  • if valid, carry on with original action

Question

Does there need to be an expiration policy for the user's secret key?

Logically the secret key is even more secure than the user's password (which never expires). It never leaves our systems (the application server and database).

I could make it expire (and generate a new one) say once a month, or once every 10 uses. But this really only seems to give value if our database (and the encryption key) has been compromised (in which case we've got bigger problems).

Are there any best practices here?

Notes / Assumptions

  • The secret key will be generated using a Java's SecureRandom class (with reseeding)
  • The secret key will be encrypted when stored on the database
  • We will ensure the one-time only use of a valid token.
    A second attempt of a valid token (within the same time window) will not be accepted.
  • The time window the token is valid for will be kept as small as possible. But it needs to be big enough for the token to be delivered (sms/email) and for the user to receive and enter token.
    We'll probably start with 90 seconds and adjust as needed.
  • The small time window should prevent brute force attacks?
    But adding a user lockout after say 10 invalid token attempts is probably a good idea.
  • Once the user has authenticated with a valid token, it's valid for the duration of their session. There will be no need for the user to re-authenticate again later.

EDIT Re: Sebastion

I considered your "KISS" approach a few weeks ago, but I was planning on going the TOTP route because it would provide greater flexibility for the future. If we ever want to use Google Authenticator (or partner with RSA, or Authy, etc.) then we're halfway there. It also simplifies our development a little since the expiration is built into the algorithm.

But ... now that I take a step back, I think your approach is probably the correct one. Over-designing and over-complicating now for a future that may never happen is a code-smell.

One thing I hadn't mentioned above was that I'd probably use something like 3 thirty second windows. So on your last point, the time frame would be 90 seconds max, or a minimum of 60 seconds, if the token was generated at the end of a window.

matt1616
  • 153
  • 4
  • 1
    Saaspass has a very nice SAML-based support for those. And many more authenticators both for evnt based and time based OTPs. – MTSan Mar 30 '16 at 19:18
  • 1
    @matt1616 I would say, f you are only going to authenticate high-risk behaviour, its better to keep it at SMS/E-mail and not allow the user to provision the token itself. If you going to do all logins, it can be a idea, but I would still suggest doing my approach until its time to enable 2FA for all logins. RSA/Authy does what I know, not use TOTP, rather some API/binary with their own protocol, thus you would gain nothing of doing TOTP if you are aiming for RSA/Authy. – sebastian nielsen Mar 31 '16 at 04:34

1 Answers1

4

I would say: You should not have a secret key at all. As you are intending to SMS or E-mail out these tokens, this means theres no need for a secret key at all.

Instead, I would suggest just random up a token, save it in database with a expiry time (like 90 seconds from now), and then send it out. Each attempt at entering the token should invalidate the token and ask the user for the opportunity to send out a new token, so each token are always "1 attempt, successful or not". Also add a Captcha to prevent someone from launching a random attack in the hope of "hitting the right code first time by just luck".

Theres no need to use TOTP protocol at all, if you are not intending to allow the user to save the seed into their own device and then generate codes "offline".

If you however allow the user to save down their seed "offline", for example to provision Google Authenticator, then it can be a good idea, but it is not industry practice, to reprovision token sometimes.

If you are going to still use a "seed" or "secret key", I would suggest using HOTP instead, eg event-based codes. Because with TOTP you will have the issue of sometimes sending out already expired tokens, because TOTP works like a clock that "ticks" each X seconds, in your case, 90 seconds. If the clock is at lets say "85 seconds" and you send it out, it will have already expired when it arrives to the user, both adding unneccessary cost to you, and also adding frustation to the user.

sebastian nielsen
  • 8,779
  • 1
  • 19
  • 33
  • Thanks a lot for your answer. I added some more comments to my question since they were too long to fit here. – matt1616 Mar 30 '16 at 15:03
  • re: "random up a token, save it in the database" ... now I'm thinking about just storing the token and expiry time in the session rather than the database. Are you aware of any issues with that approach? thanks! – matt1616 Apr 07 '16 at 20:47
  • 1
    @matt1616: Would advise against it, because that would mean theres a possible theres multiple valid codes per account located in multiple sessions. Better to save everything related to account, in the account database. – sebastian nielsen Apr 07 '16 at 20:56
  • Ok - and I guess if I wanted to do some kind of lockout after X invalid attempts, I would want to track that at the user/database level rather than at session level. Thanks – matt1616 Apr 07 '16 at 21:05
  • @matt1616 : I would suggest requiring a captcha for the account in addition to TOTP code when a incorrect password or TOTP code has entered a number of times, that will remain until a correct password, TOTP code and captcha has been entered. This prevent automated password/TOTP guessing, but still removes the need for a user to call administrator/customer service to have the account "reset". – sebastian nielsen Apr 08 '16 at 14:11