Implement Two way authentication for your website or app in php

September 24, 2014 | | Blog |

Passwords are the standard for logging in on the web, but they’re relatively easy to break. Even if you make good passwords and change them regularly, they need to be stored wherever you’re logging in, and a server breach can leak them. There are three ways to identify a person, things they are, things they have, and things they know.

Logging in with a password is single-step authentication. It relies only on something you know. Two-step authentication, by definition, is a system where you use two of the three possible factors to prove your identity, instead of just one. In practice, however, current two-step implementations still rely on a password you know, but use your phone or another device to authenticate with something you have.

If you want to secure your site or an app, you can implement 2fa (Two factor authentication) in your site in few minutes. Just simply copy and paste the code below in any text editor.

class Google2FA {

const keyRegeneration 	= 30;	// Interval between key regeneration
const otpLength		= 6;	// Length of the Token generated

private static $lut = array(	// Lookup needed for Base32 encoding
"A" => 0,	"B" => 1,
"C" => 2,	"D" => 3,
"E" => 4,	"F" => 5,
"G" => 6,	"H" => 7,
"I" => 8,	"J" => 9,
"K" => 10,	"L" => 11,
"M" => 12,	"N" => 13,
"O" => 14,	"P" => 15,
"Q" => 16,	"R" => 17,
"S" => 18,	"T" => 19,
"U" => 20,	"V" => 21,
"W" => 22,	"X" => 23,
"Y" => 24,	"Z" => 25,
"2" => 26,	"3" => 27,
"4" => 28,	"5" => 29,
"6" => 30,	"7" => 31
);

public static function generate_secret_key($length = 16) {
$b32 	= "234567QWERTYUIOPASDFGHJKLZXCVBNM";
$s 	= "";

for ($i = 0; $i < $length; $i++)
$s .= $b32[rand(0,31)];

return $s;
}

public static function get_timestamp() {
return floor(microtime(true)/self::keyRegeneration);
}

public static function base32_decode($b32) {

$b32 	= strtoupper($b32);

if (!preg_match('/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]+$/', $b32, $match))
throw new Exception('Invalid characters in the base32 string.');

$l 	= strlen($b32);
$n	= 0;
$j	= 0;
$binary = "";

for ($i = 0; $i < $l; $i++) {

$n = $n << 5; // Move buffer left by 5 to make room $n = $n + self::$lut[$b32[$i]]; // Add value into buffer $j = $j + 5; // Keep track of number of bits in buffer if ($j >= 8) {
$j = $j - 8;
$binary .= chr(($n & (0xFF << $j)) >> $j);
}
}

return $binary;
}

public static function oath_hotp($key, $counter)
{
if (strlen($key) < 8)
throw new Exception('Secret key is too short. Must be at least 16 base 32 characters');

$bin_counter = pack('N*', 0) . pack('N*', $counter);		// Counter must be 64-bit int
$hash 	 = hash_hmac ('sha1', $bin_counter, $key, true);

return str_pad(self::oath_truncate($hash), self::otpLength, '0', STR_PAD_LEFT);
}

public static function verify_key($b32seed, $key, $window = 4, $useTimeStamp = true) {

$timeStamp = self::get_timestamp();

if ($useTimeStamp !== true) $timeStamp = (int)$useTimeStamp;

$binarySeed = self::base32_decode($b32seed);

for ($ts = $timeStamp - $window; $ts <= $timeStamp + $window; $ts++)
if (self::oath_hotp($binarySeed, $ts) == $key)
return true;

return false;

}


public static function oath_truncate($hash)
{
$offset = ord($hash[19]) & 0xf;

return (
((ord($hash[$offset+0]) & 0x7f) << 24 ) |
((ord($hash[$offset+1]) & 0xff) << 16 ) |
((ord($hash[$offset+2]) & 0xff) << 8 ) |
(ord($hash[$offset+3]) & 0xff)
) % pow(10, self::otpLength);
}
}

$InitalizationKey = "coralwebdesigns";					// Set the inital key

$TimeStamp	  = Google2FA::get_timestamp();
$secretkey 	  = Google2FA::base32_decode($InitalizationKey);	// Decode it into binary
$otp       	  = Google2FA::oath_hotp($secretkey, $TimeStamp);	// Get current token

echo("Init key: $InitalizationKey\n");
echo("Timestamp: $TimeStamp\n");
echo("One time password: $otp\n");

// Use this to verify a key as it allows for some time drift.

$result = Google2FA::verify_key($InitalizationKey, "123456");

var_dump($result);