Two-Factor Authentication using SMS

Passwords have had their day. At least, that is, as the sole layer of security when protecting important information systems. With all the high-profile hacks in recent times and the majority of users still using easily-guessable passwords which are the same for multiple accounts across multiple websites, it’s so surprise that Two-Factor Authentication has gained in popularity so much in recent years.

Two-Factor Authentication works on the concept of making you prove not one thing, but two. The first – something you know – is your password. The second – something you physically have – is your mobile phone. And you prove that you have your phone by letting the website text you a random code which you enter in. It’s beautifully simple yet extremely effective. It’s not 100% completely foolproof and hack-proof, but it’s pretty darn close.

In this tutorial I’ll show you how to build a simple website login form which presents an extra two-factor authentication step for accounts which have the option switched on. You can then take the concepts and adapt it to your own system, where you might either want to enforce Two-Factor Authentication or leave it as an optional extra for your more security-conscious users!

The key concept that two-factor authentication centres around is providing the user with a single-use token (ie. a code) that we give to them on a different medium to that which they’re using to log in. Some systems rely on giving a keyfob to the user for them to keep on their keyring and lose/break whilst others either send a text message or automated phone call to the user to give them their code. In this example we’ll be sending an SMS from our application using Telecoms Cloud and their REST API.

I’ll be using PHP in this example as it’s the language I’m most familiar with, but you can adapt the concepts to whatever platform you’re most comfortable working with, and the Telecoms Cloud API can be used easily from any programming language.

Set up SMS for Two-Factor Authentication

  1. Create a free Telecoms Cloud account at www.telecomscloud.com – you’ll get £5 free credit when you sign up which is plenty to send yourself lots of test text messages whilst you develop your app
  2. Under My Account select API Settings to generate your Client ID and Client Secret that we’ll need later on
  3. From your My Numbers page, make a note of your Telecoms Cloud number so that the SMS messages sent by your application can appear to come from this number. If you don’t have one, you can pass the default SMS sender number which you’ll see in your account settings.
  4. Since we’re using PHP in this example, we’ll use the PHP Client for the Telecoms Cloud API from github.com/TelecomsCloud to make our code a bit simpler. Install and configure the Client to use the code samples in this guide.

Set up User Database for Two-Factor Authentication

I’m using a MySQL database here but you can use whatever database you like – the concepts are the same!

  1. Create a database and database user with appropriate permissions. I’m using phpMyAdmin but you can of course use whatever database manager you like.
  2. Create a table with the statement below:

I’m not covering the creation of your signup process in this tutorial, nor the turning on/off of Two-Factor Authentication. So, for the purposes of this example you can manually add a row to your database table (leave the user_id as this auto-increments) like this:

phpMyAdmin_insert

 

To generate a secure password hash for our example, you can run the following code:

Paste the output from the code above into phpMyAdmin, being careful not to include any whitespace either side of the string of text.

Build Login Forms & Script for Two-Factor Authentication

Now we’ve got our SMS account set up with Telecoms Cloud, and our user database built and a test account created, we’re ready to build the rest of the system!

Step 1: Establish connections to our local database and the Telecoms Cloud API

The first thing we’ll want to do in our script is set up the connection to our database and the Telecoms Cloud API

The path to the autoload.php file will depend on where you installed the PHP Client, check the documentation for more details. Make sure you put your Client ID and Client Secret into lines 5 and 6, and then your database username & password into lines 10 and 11.

Step 2: Handle Login Attempts

First off, start a new session with the following line:

We then want to take care of what we do if a user has submitted our login form (which we’ve not built yet – but don’t worry, that’s coming). The easiest way to do this in PHP is to check to see if the $_POST superglobal is empty or not, like this:

Since this login process is “two-factor”, it’s also “two-stage” as well. By that I mean that a user first enters their username and password then if they’re valid, the second stage is receiving the text message with a code they enter. At this point, they’re half logged in so we need some way of a) establishing that fact and b) remembering who they are between form submissions! We do this by saving their username as a session variable, but not yet storing the fact that they’re authenticated. So, the first thing we check for inside the curly braces for a POST submission is if a session variable called username exists yet:

In the code above we grab the username they entered and use this to pull some account data from our database (using the connection we established earlier on). We grab more than just their hashed password, as if it’s a successful login attempt we’ll need their mobile number to send the SMS to, so it makes sense to grab that now rather than query the database twice.

We use the password_verify function to compare the hashed version of their password which we stored in the database with the password they just entered. If the password was wrong we abort mission and present a message telling them so. Otherwise, we can continue:

We now want to make them “half” logged in, by storing their username in the session, so we start off by doing that. However, for them to be fully logged in to this app, the “authenticated” session variable must also be set. We’ll not set that until we’ve established if they have Two-Factor Authentication turn on in their account or not.

In the snippet above, we check the mfa_status setting in their account which can either be 1 or 0, depending on if it’s turned on for that user’s account. That’s obviously a policy decision for you to implement in your own application; you might make it compulsory, or you might charge extra for that feature. Either way, you’ll need to build a simple interface to allow (or implore) users to turn the feature on and verify their mobile number before saving it as their MFA device. I won’t cover that in this article, but with the concepts discussed here you will have no problems building that!

If Two-Factor Authentication isn’t turned on in the user’s account, they go from being half-logged-in to fully-logged-in rather quickly! We simply set the authenticated session variable and have done with it. Inside the first set of curly braces, though, we have the juicy stuff:

What we’re doing above is first generating a random code to send to the user’s phone via SMS, and we also save that code in the session so that next time the form is submitted we can see if the code the enter is the one we sent them.

Important: Don’t use mt_rand() in the real-world; I’ve used it here for simplicity as this is just an example (with no dependencies) to explain how Two-Factor Authentication works and how to send the code you generated as an SMS. For cryptographically secure alternatives if you’re using PHP, read the advice at http://php.net/mt_rand

We then instantiate an authenticated telecomsCloud object for the API and call the sendSMS method to send a text. There’s an optional 4th parameter which I’ve missed off here, which is the two-letter country code matching the location of the number you’re sending to. This defaults to GB so I’ve left it blank. For the 3rd parameter, from, I’ve passed a variable which is one of my Telecoms Cloud service numbers so that the SMS appears to come from that number. You’ll need to specify that somewhere in your application. If you don’t have a service number with Telecoms Cloud, you can specify the default SMS from number (which you’ll find in your Telecoms Cloud account settings) although that’s not ideal as the identity of your messages won’t be consistent with your company/brand so get your own service number if you can!

The sendSMS method returns a unique reference ID for that message which can be used to check the dispatch/delivery status (along with the time the message was delivered) if you wish, but I’ve left that out to keep the example simple.

Next, we need to turn our attention to what to do if the form is submitted and the username is set in a session variable. The only time this will happen is when a user is submitting the code they’ve received via SMS, so we’ll handle it like this:

Quite simply, we check to see if the entered code matches the one we previously saved in the session before texting it to the user. If it does – great! – we’ll set authenticated to true in the session and they’re logged in.

If the code is incorrect, we’ll output a message advising the customer, and they can start the whole login process again. (Here, you might also want to add in a mechanism that blocks their account for, say, 30 minutes if they enter an incorrect code more than 3 times, in order to frustrate anyone trying to hack their account by guessing codes.)

Step 3: Create Two-Factor Authentication Login Forms

There are three possible screens we can display to our user depending on which stage of the login process they’re at:

  1. Nothing set in session, ie. the user has just landed on the login page and hasn’t yet started logging in
  2. If they have Two-Factor Authentication enabled, the screen asking them to enter the code we sent to them via SMS
  3. On successful login (with or without Two-Factor Authentication) we’ll welcome them and then direct them to their account area

The first state we’ll check for, though, is if they’re logged in. You’ll recall above that in this application we’re considering users to be logged in if the authenticated session variable is set, and is set to true. Checking this is dead easy:

This is the welcome screen on successful authentication.
This is the welcome screen on successful authentication.

In the real world, you’d probably use a header redirect here to send them to the my account page here, but to keep the example simple I’ve simply put a welcome message and a logout button. More on that logout button later!

In the else section above we handle the logic for if the user is at the first or second stage of logging in:

If username isn’t set yet, then they must be at the very first stage of logging in so we display the login form asking for their username and password:

Two-Factor Authentication login screen, the first stage of the login process.
Two-Factor Authentication login screen, the first stage of the login process.

Otherwise, if we didn’t stop at the first hurdle (saying they’re authenticated) then we must be requiring a Two-Factor Authentication code to be entered, so we ask them to enter that code. We know the number from the code earlier on, so we can helpfully output the last 4 digits of the phone number to which we’ve sent the code, to assist the customer without giving the entire number away to any would-be hacker:

This is the screen asking the user to enter the token which has been sent to them via SMS.
This is the screen asking the user to enter the token which has been sent to them via SMS.

When that form is submitted we’ll jump to the section of PHP code validating the code entered by the user against the code stored in the session, because username is set in the session, and code is set as POST variable.

The logout button

Lastly, we’ll go right up to the top of our script immediately after the session_start(); section and add these lines:

That destroys our session and all data stored in it (eg. authenticated and username) then starts us a brand new session so the user can log in again if they need to. Any page in your application can now link users to your login form with “?logout” in the query string, and the user will be logged out.

Complete Two-Factor Authentication Code Sample with SMS and PHP:

Bringing it all together, we have the following code:

Summary

This tutorial should have given you a basic understanding of how Two-Factor Authentication works and how it adds a really good extra layer of security to users’ accounts rather than just using passwords on their own.

There’s plenty of further expansion you can do to this basic example, for example you could give users the option of having their code read to them down the phone by an automated phone call, for example.

Leave a Reply

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