Most systems currently use static passwords for identity authentication, but their security cannot meet security requirements because static passwords are easily stolen.
A dynamic password is used once in a while and the password is invalidated to prevent the security problems caused by password theft.
Dynamic passwords are divided into HOTP (dynamic password based on event count, RFC4226), TOTP (dynamic password based on time count, RFC6238), OCRA (Challenge Response dynamic password, RFC6287), etc.
This paper introduces the dynamic password authentication scheme of integrated TOTP mode, the PHP framework adopts Thinkphp3.2.3, and the dynamic password generator uses Google authtication.
1. Add oath algorithm class for thinkphp frame
The oath algorithm encapsulates the class oath.php code as follows:
? PHP/** * This are free software:you can redistribute it and/or modify * it under the terms of the GNU general P Ublic License as published by * The free Software Foundation, either version 3 of the License, or * (in your option) any
Later version. * * This are distributed in the hope that it'll be useful, * but without any WARRANTY; Without even the implied warranty of * merchantability or FITNESS for A particular purpose.
The * GNU general public License for more details. * * Should have received a copy of the GNU general public License * along and this program.
If not,
Because the seed key in Google's dynamic password algorithm uses BASE32 encoding, the BASE32 algorithm is required, and the base32.php content is as follows:
<?php//namespace Base32; /** * BASE32 Encoder and Decoder * * Last update:2012-06-20 * * RFC 4648 compliant * @link http://www.ietf.org/rfc/ Rfc4648.txt * * Some groundwork based on this class * https://github.com/NTICompass/PHP-Base32 * * @author Christian Riesen <chris.riesen@gmail.com> * @link http://christianriesen.com * @license MIT License License file */CLA SS Base32 {/** * alphabet for encoding and decoding BASE32 * * @var array/private static $alphabet = ' ABCDEF
Ghijklmnopqrstuvwxyz234567= ';
/** * Creates an array from a binary string into a given chunk size * * @param string $binaryString string to chunk * @param integer $bits number of bits per chunk * @return Array/private static function chunk ($binaryString, $bi
TS) {$binaryString = Chunk_split ($binaryString, $bits, "); if (substr ($binaryString, (strlen ($binaryString))-1 = = ") {$binaryString = substr ($binaryString, 0, strlen ($binar
ystring)-1);
} Return explode (", $binaryString);
/** * encodes into BASE32 * * @param string $string Clear Text String * @return string BASE32 encoded string
* * public static function encode ($string) {if (strlen ($string) = = 0) {//gives an empty string return ';
//Convert string to binary $binaryString = '; foreach (Str_split ($string) as $s) {//return each character as a 8-bit binary string $binaryString. = sprintf ('%0
8b ', Ord ($s));
}//Break into 5-bit chunks, the then break "into" an array $binaryArray = Self::chunk ($binaryString, 5);
Pad array to is divisible by 8 while (count ($binaryArray)% 8!== 0) {$binaryArray [] = null;
$base 32String = ';
Encode in Base32 foreach ($binaryArray as $bin) {$char = 32;
if (!is_null ($bin)) {//Pad the binary strings $bin = Str_pad ($bin, 5, 0, str_pad_right);
$char = Bindec ($bin);
}//Base32 character $base 32String. = self:: $alphabet [$char]; } return$base 32String;
/** * Decodes base32 * * @param string $base 32String Base32 encoded String * @return string Clear text string */public static function decode ($base 32String) {//only work in upper cases $base 32String = strtoupper ($base 32Str
ing);
Remove anything this is not base32 alphabet $pattern = '/[^a-z2-7]/';
$base 32String = preg_replace ($pattern, ', $base 32String);
if (strlen ($base 32String) = = 0) {//gives a empty string return ';
$base 32Array = str_split ($base 32String);
$string = ';
foreach ($base 32Array as $str) {$char = Strpos (self:: $alphabet, $STR);
Ignore the padding character if ($char!==) {$string. = sprintf ('%05b ', $char);
} while (Strlen ($string)%8!== 0) {$string = substr ($string, 0, strlen ($string)-1);
$binaryArray = Self::chunk ($string, 8);
$realString = ';
foreach ($binaryArray as $bin) {//Pad per value to 8 bits $bin = Str_pad ($bin, 8, 0, str_pad_right); // Convert binary strings to ASCII $realString. = Chr (Bindec ($bin));
return $realString;
}}?>
Place these two files in the Thinkphp\library\vendor\oath directory of the thinkphp framework, and the oath directory is created by itself.
2. Add Database Fields
The user table adds the following fields:
Auth_type (0-static password, 1-dynamic password)
Seed (seed key)
Temp_seed (temporary seed key)
Last_logintime (last logon success time)
LAST_OTP (last use password)
The auth_type is to indicate which authentication method is used by the user, seed is the seed key of the user, Temp_seed is a seed key that is temporarily saved before the user is opened, if the user opens the dynamic password authentication successfully, the field content will fill in the seed field. Last_logintime and LAST_OTP are the time and dynamic passwords that were successful for the last authentication to prevent users from reusing the same password.
3, Code integration
1), open dynamic password
In the original system of the modified Password page, plus the selection of authentication methods, such as:
If the user chooses a dynamic password method, a two-dimensional code is generated to display on the page for the user to open the dynamic password. To be compatible with Google Authtication, the second dimension code format is the same as Google. The method of generating two-dimensional code see my another article "Thinkphp3.2.3 Integration Phpqrcode generation with logo two-dimensional code."
The generated key two-dimensional code code is as follows:
Public Function QRCode ()
{
vendor (' oath.base32 ');
$base = new \base32 ();
$rand = random (16);//Generate random seed
$rand = $base 32->encode ($rand);
$rand =str_replace (' = ', ', $rand);//Remove padding ' = '
$errorCorrectionLevel =intval (3);/fault tolerance level
$matrixPointSize = Intval (8);//Generate picture size
//Generate two-dimensional code picture
vendor (' Phpqrcode.phpqrcode ');
$object = new \qrcode ();
$text = sprintf ("otpauth://totp/%s?secret=%s", $user, $rand);
$object->png ($text, False, $errorCorrectionLevel, $matrixPointSize, 2);
The generated seed $rand saved to the Database temp_seed field
}
Random is the generation of random string functions. The code for the $rand =str_replace (' = ', ', $rand) is because the BASE32 decoding algorithm in the Google phone token does not have the fill ' = ' number.
The code to verify the user's dynamic password is as follows:
Read Temp_seed
vendor (' Oath.oath ') from the database;
$object = new \google2fa ();
if ($object->verify_key ($temp _seed, $OTP)) {
validation succeeded, update the database with seed for Temp_seed,auth_type 1,LAST_OTP for OTP
}
2), dynamic password login
User Dynamic Password logon authentication code:
Reads the AUTH_TYPE,SEED,LAST_OTP field from the database.
if ($auth _type==1) {//Dynamic password
//Prevent duplicate Authentication
if ($lat _OTP = = $OTP) {
dynamic password repeat use return
}
vendor (' Oath.oath ');
$object = new \google2fa ();
if (! $object->verify_key ($seed, $OTP))
{
dynamic password is incorrect
}
else
{
login succeeded, update database LAST_OTP to $ Otp,last_logintime is time ()
}
}
4. Test verification
Download Google Authtication, use the static password login system, access to modify the Password page.
Open Google Authtication, scan the two-dimensional code, will display a dynamic password.
Save content, open dynamic password success!
Then you can log on to the system with a big, dynamic password!
The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.