Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

I am currently writing a TOTP implementation in PHP (but this applies to other languages as well) and of course want it to be as secure as possible, what should be considered?

After reading the relevant RFC 6238 and RFC 4226 my implementation currently looks like this:

  • Every single used code is blacklisted for 2 minutes, independent of whether it was successful or not
  • The secret is generated by a secure random number generator
  • The QR code is generated on my machine and not handed over to third party providers
  • Entering a reCaptcha is required in order to prevent brute force attacks
  • Codes are validated using a timing attack resistant comparison function
  • A total of 5 codes are checked (the current, the last two and the next two)
  • A single 16-character rescue code is generated (by a secure random number generator) and only shown once after successful activation, it cannot be retrieved by the user afterwards
  • Users are asked only once every seven days on a known machine (by setting a cookie)
  • Secrets and Rescue codes are stored unencrypted in the database, but this should not be an issue as the script needs to decrypt it anyway

Am I missing some important? Are some of the points a bad decision?

share|improve this question

2 Answers 2

up vote 1 down vote accepted

Users are asked only once every seven days on a known machine (by setting a cookie)

You will need to be very careful here, as such a cookie would be effectively a backdoor bypassing the 2-factor requirement. An attacker would need to wait until a user authenticates, steal their cookie, and now they don't need to worry about 2-factor auth any more. You can strengthen this by either tying the cookie to the IP address (highly unreliable, since people hop wireless providers all the time), to a browser ID string (very easy to defeat), or to a GeoIP region (more reliable than to an IP, but also less secure and not fool-proof).

FYI, I wrote a Python/CGI TOTP implementation (https://github.com/mricon/totp-cgi), which accepts simple REST calls. It may be something you can use instead of rewriting it entirely in PHP.

share|improve this answer
    
Thanks for your answer. I thought about it in the last days. Technically speaking the cookie still is a second factor (“something you have”), but I think that I’ll tie it to the first few blocks of the IP address, additionally to secure and httpOnly. –  TimWolla Jan 6 at 20:26

One may argue that unencrypted secrets, stored "as is" in the database, may be an easy prey to SQL injection attacks. Though conceptually the secrets are anyway accessible from the running server, it may be worthwhile to protect them a bit more, so that attackers will have to go beyond the "custom SQL" stage. For instance, instead of storing the secret S and the rescue code C for each user in the database, store for each user a random value R, and compute the secret S and rescue code C with HMAC over R, using a server-wide secret key stored in a configuration file, not in the database. Use HMAC/SHA-1 over "secret||R" (concatenation of the ASCII string "secret" and the random value R) to get the actual user secret S; for the rescue code, use HMAC/SHA-1 over "rescue||R".

Other people may argue that PHP is not the best language for cryptography. In fact, high-level languages are ill-suited for things like "constant-time comparison functions". To get some real control over implementation issues (all side-channel leaks, including timing), a lower-level language is advisable (even C# or Java would be "lower-level" in that sense).

Blacklisting all submitted codes may increase vulnerability to DoS; someone may spam you with lots of requests so as to make your server remember lots of blacklisted codes. Similarly, your description does not seem to include prevention measures again such heavy spamming. You may want to, at least, refuse to talk to IP addresses which have sent you more than ten requests in the last ten seconds or so.

Implementing cryptography properly is difficult; doing it again means taking extra risks of implementation-related issues. Doing your own code is great pedagogy; but if you want to use the code in production, then maybe you should first try to see if there is no existing implementation which already fits your bill.

share|improve this answer
    
Thanks for your answer. Some further explanation to clarify my decisions: 1. I'm using Prepared Statements, so SQL injections should not be an issue. In case anyone gets into my database I have larger problems anyway. 2. Obviously PHP is not the best language for cryptography, but as this is an extension to an existing software there isn't any room for other language, unfortunatly. 3. The blacklisting issue is a valid point. The only current protection is the Captcha. 4. I am using a library for the lower level TOTP generation, I also extended it with proper security measures (Secure RNG etc.) –  TimWolla Jan 2 at 20:20

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.