Tableless and Secure One-Time Password (OTP)

A one-time password (OTP) is a password that is only valid for a single login session or transaction. It is commonly used in the internet for registration and password reminder process in which OTPs are provides to the user in a form of a link that the user uses to access in order to create/reset his password.

Common requirements of One Time passwords are:

  1. Statistically unique – using the same password for all requests is probably not the right security choice.
  2. Hard to guess – using sequential number is again, probably not the right security choice.
  3. Can be authenticated by the server – the server needs to distinguish between real OTP and bogus OTP.
  4. Good for one time – after the process is done the OTP should no longer be valid.
  5. Time limited – the OTP usually expires after a configurable amount of time.
  6. Secure – hackers should have a hard time changing the expiry date, username context and so forth.

Most OTP implementations use a Database table to persist the OTP and to manage their expiry date, a DB table might look like this:

id User Id OTP Expire date
1 Amir Asfsd3434bgddh 1/1/2010
2 Someone Ddfsd3345ssfsss 7/1/2010

While this is a valid solution, it is not the most efficient and elegant one, the truth is that you do not need an additional table enable and manage OTPs.

The answer is simple – the seed for this OTP is already persisted in the Database in the form of the old password (or more exactly the old password hash)

Here is how it is done:

We will use a password reminder process as an example so the base URL would be http://somedomain.com/resetpass/ and use a restful API for the rest of the parameters

The major challenged is how to generate a new OTP for each user that is unique and can be authenticated without persisting it to the database.

We start by using old password hash as the seed from which we can generate the new One Time Password, let’s call it OTP and let OTP = hash(old_password). We also add the user id, and call it userID as a context for the user who wants to reset his password.

The URL will now be http://somedomain.com/resetpassword/userID/OTP

Now, that of itself does not satisfy the all the requirements, it does not provide a time limitation, so let’s add the time of expiry in the URL. So let TIME = expiry_time_in_milliseconds and the URL should look like this:

http://somedomain.com/resetpassword/userID/TIME/OTP

So now the URL meets the Can be authenticated by the server and the Time limited requirements, now let’s make it a little bit more secure so that we satisfy the rest of the requirements.

We start by adding the expiry date as a part of the OTP so that hackers would have a hard time changing the time and breaking the expiry date. So let OTP = hash(hash(old_password)+TIME)

Next let’s add the User Id to the mix, so that hackers would have a hard time changing the user context. So let OTP = hash(hash(old_password)+TIME+userid)

To add additional layer of security we would add a unique application key (we will call it SALT) so that we have a key only the application know. So let OTP = hash(hash(old_password)+TIME+userid+salt)

When a server receives a request for reset password, it will get the following parameters:

  • userID = the ID of the user that wants to reset the password.
  • Time = the expiry time of the request (after which time the request is not valid)
  • OTP = hash(hash(old_password)+TIME+userid+salt)

All the server needs to do in order to authenticate the request is to perform its own hash(hash(old_password)+TIME+userid+salt) where hash(old_password) , time, user id and salt are all already known and to verify that the result is identical to the received OTP from the request. If a hacker changes the user id or the time then their OTP would not be the same as the OTP generated on the server and the request will be invalidated.

As for the good for one time requirement – Ones the user changes his password then the OTP will be invalidated because the hash(old_password) would not be the same on the server, so in fact this is a one time password.

I created a reference implementation using CakePHP and released it as open source – check it out.

Share the love...Tweet about this on TwitterShare on LinkedInShare on Google+Share on Facebook

Amir Shevat

Amir Shevat is the global Startup Outreach lead in Google Developer Relations (g.co/launch). Previously, Amir Led Google Campus Tel Aviv and was the co founder of several startups.

You may also like...

12 Responses

  1. T0aD says:

    Very interesting system, however it doesn’t completely isolate the problem of ‘storing the state of the OTP’, basically the OTP lifespan is represented by the password of the user.
    There are many places outside that area that needs an OTP that wouldn’t work with this logic.

    This also means that even when you don’t need to store the OTP in the database during checks like in your case (which is very good), you still need the database to store the fact that the OTP is used up.

    BUT (!) this means its an excellent solution for everything that is targeting a value in a database (could be something else like the email of the user). Nice article!

    • seamoon_alice says:

      Very interesting system, however it doesn’t completely isolate the problem of ’storing the state of the OTP’, basically the OTP lifespan is represented by the password of the user.
      There are many places outside that area that needs an OTP that wouldn’t work with this logic.

      This also means that even when you don’t need to store the OTP in the database during checks like in your case (which is very good), you still need the database to store the fact that the OTP is used up.

      BUT (!) this means its an excellent solution for everything that is targeting a value in a database (could be something else like the email of the user). Nice article!

  2. Majic says:

    very smart idea with clear instructions – I have bookmarked this for a future reread + full tryout

    thanks very much :)

  3. Jahufar says:

    Very clever – solves all the problems with securely resetting a hashed password in one fell swoop.

    Thanks for posting this!

  4. Bill says:

    Can you elaborate on how an attacker would change user context? Adding user id to the hash doesn’t seem to add additional security to me.

    • Amir Shevat says:

      Hi Bill – Adding the user ID to the hash protects against OTP hijacking – without it an attacker can request and OTP for his user, then change the user ID to someone else and replace their password… adding the ID to the hash prevents that.

  5. Saleh says:

    thanks amir, perfects

  6. chet says:

    Amir,

    Nice solution for OTP….how does this ensure that the OTP is not repeated…is there a way to ensure that?

    Thanks,
    Chet

  7. anonymous says:

    Amir – thanks for the heads up. This looks like a very clever solution!

    I was initially concerned with the security, as;

    I know the user I want to “hack”
    I know today’s date.
    I might guess the old password

    SO it is possible I know or guess most of the puzzle, and could generate my own hash to pass in along with my spoofed time. Though I would think that the remaining salt and hash algorithm used should add enough variability to prevent such intrusions.

    I am very interested to see some feedback from a security professional, because this looks like a great solution to a common need.

  8. Amir Shevat says:

    Thanks for your feedback!

    The puzzle has 5 parts:
    1) User (semi public)
    2) Time (public)
    3) User old password (super private)
    4) Application salt (private)
    5) hash function (public)

    The Key is the old password – if you know the old password as a hacker then it is ‘game over’ – You can just login and do whatever you want (like change the password and email in the account area). If you do not know the password then a combination of the password and the application salt should be a strong seed for the one time password. Right?

    Cheers
    Amir Shevat

  9. anonymous says:

    thanks for this pretty clear and simple post on this topic, which is mostly known if you look at ‘attempts’ (i like yours a lot) by openid, twitter, fb auth etc…

    But yeah, nice i love the numbered list description and outline of it thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *