1

I am implementing the password reset functionality using OTPs (one-time passwords). When the user clicks on the 'forgot password' link, he / she is sent an OTP and is redirected to the OTP input page. After typing in the OTP, the user is then redirected to the 'update password' page.

How should I prevent any user from bypassing the OTP input page and going directly to the 'update password' page?

I know I can store the fact that the OTP input is valid in the database, and then I can check that when the 'update password' page is accessed. Are there any alternative / preferred methods to this?

This question is about how best to achieve the desired workflow without allowing the user to bypass any step. It is not related to how resistant OTPs are to attack.

  • 2
    Possible duplicate of [One-Time-Passwords resist against bruteforce attacks? Immune Alternative?](https://security.stackexchange.com/questions/70571/one-time-passwords-resist-against-bruteforce-attacks-immune-alternative) – yeah_well Aug 08 '19 at 03:34
  • How does the user receive the OTP token? Are you using an authenticator app, or sending it via sms, email? – Eamonn McEvoy Aug 08 '19 at 18:44
  • @EamonnMcEvoy at the moment, by email – David Whitlock Aug 09 '19 at 02:12

2 Answers2

1

Your 'update password' page should contain three fields: old password, new password and repeat new password. When visited through 'forgot password' link it should contain OTP input field instead of old password input field. You probably should not split this via redirect or something if you can help it.

1

Sounds like there is business logic that you want to implement. The specifics will depend on the language or framework you have used to build your web application. I suggest that you have your pages or routes that need to be authenticated (ie private) and those that do not (ie public). The "update password" page is private, the OTP page is public. This means that no one can get to the update password page unless they have been authenticated.

The workflow then is the user lands on your public OTP page. Anyone can get there and anyone can enter any OTP. Your target user types in their OTP. You trust the OTP* and therefore you trust that that the OTP is a suitable and robust authentication token. At that point your code (your controller if you are using an MVC or whatever your app is) signs in the user. You know the user's ID because you have said that you record the OTP in a database.

Now your user is a legitimate signed-in user and you can redirect from your route to the update password page.

You didn't say that you wanted to prevent the user going anywhere other than the update password page, but if you want that to be the only place they can go then you will need to have a flag that says this user is at the post-OTP stage and then your routes will have to check for this and deny access to any route if the user is a post-OTP user. You will have to reset the OTP flag after they have updated the database. I don't think I'd bother as it seems like a lot of effort to code for marginal benefit, but if you wanted to then you could either check in each page or if you use Django you can decorate the view function or use whatever helper your framework gives you.

* You did specifically put any discussion of OTP resistance to attack out of scope, but I think that if you do use the OTP as a unique identifier then you should ask for the user's username/email address / phone number on the OTP page and you should take into account that OTPs are short (low entropy) and therefore you may want to prevent brute-force, you then use the combination of OTP + UID to identify the user. Obviously a short OTP is worse than a long passphrase so you will need to build controls against that but I won't go into those. If you want to go directly from the "forgot password" page then you can redirect after your "we have sent you a OTP code if we have your details" confirmation and you could use a server-side variable to ensure that you know the UID entered by the user who wants to reset their password, but as the forgot password page is public and you can't trust that user then I can't see any disadvantage to asking for the UID/email or even including it in the headers.

Unicorn Tears
  • 1,189
  • 4
  • 6