This post describes an implementation of two-factor authentication with PHP using the time-based one-time password algorithm (TOTP). This method is standardized for the Web in RFC 6238, described here, and is widely used as an additional security measure for logins. To use it, you need an independent second factor, in this case a smartphone app generating a code that must be entered at login in addition to the password. The code is “one-time” in the sense, that it is only valid within a time window of (typically) thirty seconds. There are different apps to choose from, maybe best known ist the Google Authenticator. See a list of possible apps at the end of this post, but there are many more.

To implement this, I used the open-source PHP library GoogleAuthenticator (GitHub), which, despite its name, works with any app implementing the standard. For security reasons, I also used the library PHP QR Code, requiring the GD library to be installed.

Initialization

To initialize the authentication, a secret has to be created for every user and has to be stored together with the other user data on the server. It consists of a string of uppercase letters and digits of arbitrary length, which is used as the base value for the login code generation.

To generate a secret, the following code can be used:

$auth = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
$secret = $auth->generateSecret();

You can also create your own secret for a user as a string of at least 16 randomly generated characters, consisting of uppercase letters (A-Z) and the digits 2 to 7 (called a Base32 encoding: see RFC 4648 and Wikipedia for more information).

With the additional information of an issuer (e.g. a website or company name) and a username, an URL is created to transfer the secret to the authenticator app. Issuer and username are not used for the generation of the login code, but merely as meta information to be displayed in the app and to identify the account. The URL can be created as follows:

$secret = 'XVQ2UIGO75XRUKJO';
$issuer = "d3v.one";
$username = "Alice";
$url = 'otpauth://totp/'.$issuer.':'.$username.'?secret='.$secret.'&issuer='.$issuer;

With this URL a QR Code is created, that can be scanned by the authenticator app. In our example, we use the secret “XVQ2UIGO75XRUKJO”, the issuer “d3v.one” and the username “Alice” to create the following QR code:

In the Google Authenticator library, an external web service is used to create the image of the QR code. I consider this a security breach, as the secret is transferred over the internet to an external website that is not under your control.

Instead, I used the library PHP QR Code with the following code to create the QR code image from the URL:

ob_start();
QRCode::png($url, null, QR_ECLEVEL_M, 5, 0, false);
$png = base64_encode(ob_get_contents());
ob_end_clean();
echo '<img src="data:image/png;base64,'.$png.'">';

Authentication

To test the example code, install one of the Authenticator apps, and scan the above QR code. From now on, every 30 seconds a new login code should be generated in the app, each one valid for the next 30 seconds.

At any given time, the valid login code can be generated with

$auth = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
$code = $auth->getCode($secret);

and can be compared with the user input to validate the login. As a live example, the valid login code for the above example secret is shown below and updated every five seconds. This should be more or less synchronous with the displayed code in your app:

Considerations

The shared secret must be kept secret at any time. Knowledge of the secret allows an attacker to create valid codes at will.

Another problem could be, that the server time and the smartphone time are not in sync. Nowadays, with automatic time sync in every connected device, this shouldn’t be a common problem.

As an implementation detail, the transferred code is typically not only compared to the valid one but additionally to the previous one. As entering and transmitting the code takes some time, the transmitted code may already have become invalid.

A practical consideration for choosing the right Authenticator app may also be, how easy it is to transfer the secret(s) to a new smartphone or a different device, ideally in a secure way. And maybe even more important: is it possible to create a backup?

All in all, the TOTP algorithm is a quick and easy method to add additional security to the login process.

Authenticator Apps

Google Authenticator (Android, iOS)

Microsoft Authenticator (Android, iOS)

Twilio Authy (Android, iOS, requires account creation with phone number and email)

Aegis Authenticator (Android)

FreeOTP Authenticator (Android, iOS)

Sophos Authenticator (Android, iOS)

Authenticator Pro (Android)