Multi-factor authentication and Sinatra ROTP

My entry for Fivium Hack Day 2015

Posted 9th September 2015 23:08 byBen Basson

I recently took part in Fivium Hack Day 2015 and as usual, I hadn't really thought enough about what I was doing prior to the event. I initially started out on an overly ambitious attempt to bring together lots of data from different products that we use (including Jira, Crucible and Perforce), but I quickly realised that learning all of their APIs and implementing everything I'd dreamt up was way beyond the scope of a 24-hour event. I'm planning to come back to that project at a later date, so keep an eye out for more on that.

After several elapsed hours, a pizza and some beer, I was having a chat with one of the company directors about new hack ideas. In the end, I decided to change tack and have a go at building a multi-factor authentication implementation that we could plug into our systems.

It was a bit too much like work for a hack day, but I thought it was interesting and I was running out of time to do anything much more complex. I also wanted to win something, and it seemed like an idea I could polish up quite nicely for a demo.

A forenote on passwords

Passwords suck for (at least) the following reasons:

  • To be secure, you must use a new password for everything you sign up to
  • Every website and app has a different idea of what constitutes a "good" password
  • Often those websites are wrong and impose awful artificial limitations on what you can enter (usually length, but the most bizarre one I've seen recently prevented '@' being used)
  • "Good" passwords are impossible to learn or remember
  • It's relatively easy to trick someone into giving up their password
  • Passwords usually expire periodically and must be changed

Troy Hunt's article "The only secure password is the one you can't remember" will say it better than I ever could:

You need a dedicated password management system, pure and simple. There is just not another practical and secure way of dealing with it in the current day.

The only reasonable way to manage passwords is to use a password management system. My personal choice is LastPass, but there are plenty of alternatives such as 1Password and KeePass.

Another great way to manage passwords is to avoid them completely in the first place by logging into websites with your Google account (or Facebook, or Twitter, etc). You should be careful to make sure that whatever you're logging in to doesn't do anything you're not happy for it to do (like post messages on your behalf) but that's usually obvious.

Multi-factor authentication

So having established that passwords suck, what is multi-factor authentication all about? It sounds fancy, but really it's just about having more than one password. Given my contempt for passwords and the problems listed above, why would anyone want to have an additional password for a website?

There are many reasons to impose additional controls on logging into user accounts, but in most cases multi-factor authentication is used to protect against scenarios where the primary password has been compromised (stolen or derived). In effect, multi-factor authentication is an admission that passwords are terrible.

To see how it's typically used, let's take Google's implementation as example.

Whenever I log into my Google account from a new device, I enter my email address and password. Once I've done that, Google detects that I've enabled multi-factor authentication and I'm prompted for a verification code, like so:

Screenshot of Google's two-factor authentication

The verification code is generated by an application on my phone called Google Authenticator, and is valid for only 30 seconds at a time. Other potential sources of a verification code involve grid cards, security tokens and simple text messages (with a validity window of a few minutes).

In each scenario, the idea is that the person logging in must have the additional app, card or device to be able to produce the necessary code. The likelihood of an attacker being able to produce both the password and the required code is incredibly low, because if someone's mobile phone or secure token is stolen (compromising the code), they're going to notice and hopefully do something about it (like change important passwords).

The codes are also usually only valid for a short period of time, so a code that's stolen during transmission would be useless after, say, 30 seconds.

Most web pages allow the user to "remember" the device they're on, so you can essentially trust devices like your home computer and therefore bypass entering this extra code each time. This is usually fine, as you can easily revoke that trust later if the device is lost or stolen, and you're still protected against login attempts from other devices (i.e. attackers using their own computer).

Should I use multi-factor authentication?

Yes, you should. I would recommend enabling it for (at minimum) your Google account(s), Facebook, Twitter and any online password management services. There is an app for every major mobile operating system (yes, including Windows Phone!) and they're simple to use.

Remember to also enable other backup methods, like code cards you can print out and text messaging, in case you lose your phone.

Should I implement multi-factor authentication on my web app?

It really depends on the risk of an account being breached and the impact involved.

Aside from that, if you do implement multi-factor authentication, you should really think about what it is you're trying to prevent. A recent example of a security incident at Mozilla that could have been avoided by using multi-factor authentication comes to mind:

It is somewhat surprising that Mozilla did not enforce two-factor authentication for its sensitive information in the past. Without two-factor authentication, all the attacker needed was one set of credentials to gain access.

The article contains more information, but the short story is that someone used a password on more than one site (not a good idea) and didn't activate the two-factor authentication feature on their Bugzilla account. That password was then involved in a separate breach, giving full access to their Bugzilla account. Information about security holes in Firefox was then accessible to the attacker. Oops!

The lesson to be learned here: If you do implement two-factor authentication, it's worth considering whether to limit administration or other privileged functions on the system to user accounts that have enabled it.

How does multi-factor authentication work?

There are various ways to do multi-factor authentication. Google's implementation uses a Time-based One-time password algorithm (TOTP) that basically works like this:

Time (UTC) + Secret = 6 Digit Code

In order to set up the Google Authenticator app on my phone and enable multi-factor authentication, Google required me to scan a QR code containing a secret key that they had generated for me. With that secret key, and the current time, the app has all the information it needs to give me the 6-digit code. Google simply repeats the same process on their end to verify the code that I type in matches their expectation.

The key weakness of the algorithm is keeping the secret key a secret. If anyone managed to get hold of the secret key, the game is up and they'd be able to generate any code for any point in time. The apps store this information on the phone, but the keys are not visible in the app, so it's not like you could quickly read the value from someone's phone without investing some time plugging in a USB cable and finding it on the device storage.

The counter to this weakness is that you still need the password, and so an attacker has to compromise both the password and the secret key, which is possible but unlikely.

Sinatra ROTP

At a hack day, I usually can't resist wheeling out Sinatra and HAML for rapidly prototyping simple web pages, and this time was no different. I decided that it would be quick and easy to build a REST API using Ruby which could then be invoked over HTTP from any other technology stack.

In a real implementation, this is a bit contrived and really I would expect that someone would just implement TOTP in their language of choice (or find an existing library).

Ruby provides a lot of useful gems, and it was quick and easy to find the ROTP gem that implements TOTP. That saved me a bunch of time, as all I really needed to do was wrap the various bits of functionality in an API and then quickly integrate it with another system as a proof of concept.

As I find it hard to name things, I simply just called the end product Sinatra ROTP, combining the names of the two key components I'd strung together.

While I can't / don't want to show screenshots of the system that I quickly hacked up an integration with (mostly due to it being a complete hack day horror show) I have subsequently tidied up the landing page to show a worked example that updates itself as you change the values:

Screenshot of Sinatra ROTP landing page

Feel free to play around with it, and check out the source code on the sinatra-rotp page on GitHub.

The results

Prior to the hack day, I had only basic knowledge of how all of this stuff worked. I knew the basics of the algorithm, but I'd never actually spent any time thinking about it. Now I can safely say that I understand multi-factor authentication.

Unfortunately, and unsurprisingly, I didn't win the main prize (a Parrot AR.Drone 2.0), but I did win a pretty kick-ass t-shirt:

Awesome fox t-shirt

Another nice thing to come out of this is that we're likely to take the knowledge and prototype forward to implement multi-factor authentication in some of our systems.

Overall, this was a neat little project and I've stuck it all up on GitHub and Heroku in case it's of interest to anyone.