One Time Passwords with PHP
From FreeAuth Wiki
Contents |
[edit] Background
There are 3 problems with most authentication methods:
- Using static passwords that anyone that knows can also use to commit fraud and login as you. Most people have relatively few passwords, so reuse the same passwords between sites.
- Using static information such as URLs or hashes, that can't directly be used to authenticate you, but can be used to track you like cookies in browsers
- Using a central database to authenticate you, the central database can track your logins.
[edit] Solution
One solution that hasn't fully been grasped yet is using One Time Passwords (or OTPs) as a strong authentication method to different sites, although OTPs have been around for a while, very few people use them as static passwords are easier for most people.
At the time of writing I was using mobile OTP in my java enabled phone, but at time of writting this only allows me to have 1 hash stored at a time. While this is nice I've been corresponding with the author of the software to enhance it by allowing you to store multiple hashes with memoriable aliases (ie the site or server name), or let you generate a new hash if you are going to a new website. If someone is very familiar with java midlets I'd be interested in hearing from you to help extend this wonderful application.
[edit] Some PHP Code
Below is some PHP code I wrote for the CAcert website to enable OTP logins.
function getOTP64($otp)
{
$lookupChar = "123456789abcdefhkmnprstuvwxyzABCDEFGHKMNPQRSTUVWXYZ=+[]&@#*{}.%:";
for($i = 0; $i < 6; $i++)
$val[$i] = hexdec(substr($otp, $i * 2, 2));
$tmp1 = $val[0] >> 2;
$OTP = $lookupChar[$tmp1 & 63];
$tmp2 = $val[0] - ($tmp1 << 2);
$tmp1 = $val[1] >> 4;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 63];
$tmp2 = $val[1] - ($tmp1 << 4);
$tmp1 = $val[2] >> 6;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 63];
$tmp2 = $val[2] - ($tmp1 << 6);
$OTP .= $lookupChar[$tmp2 & 63];
$tmp1 = $val[3] >> 2;
$OTP .= $lookupChar[$tmp1 & 63];
$tmp2 = $val[3] - ($tmp1 << 2);
$tmp1 = $val[4] >> 4;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 63];
$tmp2 = $val[4] - ($tmp1 << 4);
$tmp1 = $val[5] >> 6;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 63];
$tmp2 = $val[5] - ($tmp1 << 6);
$OTP .= $lookupChar[$tmp2 & 63];
return $OTP;
}
function getOTP32($otp)
{
$lookupChar = "0123456789abcdefghkmnoprstuvwxyz";
for($i = 0; $i < 7; $i++)
$val[$i] = hexdec(substr($otp, $i * 2, 2));
$tmp1 = $val[0] >> 3;
$OTP = $lookupChar[$tmp1 & 31];
$tmp2 = $val[0] - ($tmp1 << 3);
$tmp1 = $val[1] >> 6;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 31];
$tmp2 = ($val[1] - ($tmp1 << 6)) >> 1;
$OTP .= $lookupChar[$tmp2 & 31];
$tmp2 = $val[1] - (($val[1] >> 1) << 1);
$tmp1 = $val[2] >> 4;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 31];
$tmp2 = $val[2] - ($tmp1 << 4);
$tmp1 = $val[3] >> 7;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 31];
$tmp2 = ($val[3] - ($tmp1 << 7)) >> 2;
$OTP .= $lookupChar[$tmp2 & 31];
$tmp2 = $val[3] - (($val[3] - ($tmp1 << 7)) >> 2) << 2;
$tmp1 = $val[4] >> 5;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 31];
$tmp2 = $val[4] - ($tmp1 << 5);
$OTP .= $lookupChar[$tmp2 & 31];
$tmp1 = $val[5] >> 3;
$OTP .= $lookupChar[$tmp1 & 31];
$tmp2 = $val[5] - ($tmp1 << 3);
$tmp1 = $val[6] >> 6;
$OTP .= $lookupChar[($tmp1 + $tmp2) & 31];
return $OTP;
}
function checkOTP($username, $password)
{
$username = mysql_real_escape_string($username);
$password = mysql_real_escape_string($password);
$query = "select * from `users` where `username`='$username'";
$result = mysql_query($query);
if(mysql_num_rows($result) > 0)
{
$otp = mysql_fetch_assoc($result);
$otphash = $otp['otphash'];
$maxperiod = 3; // in minutes +/- 3 minutes
$time = round(gmdate("U") / 60);
$query = "delete from `otphashes` where UNIX_TIMESTAMP(`when`) <=
UNIX_TIMESTAMP(NOW()) - 600";
mysql_query($query);
$query = "select * from `otphashes` where
`username`='$username' and `otp`='$password'";
if(mysql_num_rows(mysql_query($query)) <= 0)
{
$query = "insert into `otphashes` set `when`=NOW(),
`username`='$username', `otp`='$password'";
mysql_query($query);
for($i = $time - $maxperiod; $i <= $time + $maxperiod; $i++)
{
if(strlen($password) == 8)
$md5 = getOTP64(md5("$i$otphash"));
else
$md5 = getOTP32(md5("$i$otphash"));
if($password == $md5)
return(true);
}
}
}
return(false);
}
[edit] SQL Tables
In my particular implementation I use SQL for the information storage which is a lot of respects is more secure and efficient then using flat files, especially with shared systems.
CREATE TABLE `otphashes` ( `when` datetime NOT NULL, `username` varchar(255) NOT NULL, `otp` varchar(255) NOT NULL ); CREATE TABLE `users` ( `username` varchar(255) NOT NULL, `otphash` varchar(255) NOT NULL );

