Edit File by line

Deprecated: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in /home/sportsfever/public_html/filemanger/function.php on line 93
/home/sportsfe.../httpdocs/wp-conte.../plugins/wordfenc.../modules/login-se.../classes/controll...
File: totp.php
<?php
[0] Fix | Delete
[1] Fix | Delete
namespace WordfenceLS;
[2] Fix | Delete
[3] Fix | Delete
class Controller_TOTP {
[4] Fix | Delete
const TIME_WINDOW_LENGTH = 30;
[5] Fix | Delete
[6] Fix | Delete
/**
[7] Fix | Delete
* Returns the singleton Controller_TOTP.
[8] Fix | Delete
*
[9] Fix | Delete
* @return Controller_TOTP
[10] Fix | Delete
*/
[11] Fix | Delete
public static function shared() {
[12] Fix | Delete
static $_shared = null;
[13] Fix | Delete
if ($_shared === null) {
[14] Fix | Delete
$_shared = new Controller_TOTP();
[15] Fix | Delete
}
[16] Fix | Delete
return $_shared;
[17] Fix | Delete
}
[18] Fix | Delete
[19] Fix | Delete
public function init() {
[20] Fix | Delete
[21] Fix | Delete
}
[22] Fix | Delete
[23] Fix | Delete
/**
[24] Fix | Delete
* Activates a user with the given TOTP parameters.
[25] Fix | Delete
*
[26] Fix | Delete
* @param \WP_User $user
[27] Fix | Delete
* @param string $secret The secret as a hex string.
[28] Fix | Delete
* @param string[] $recovery An array of recovery codes as hex strings.
[29] Fix | Delete
* @param bool|int $vtime The timestamp of the verification code or false to use the current timestamp.
[30] Fix | Delete
*/
[31] Fix | Delete
public function activate_2fa($user, $secret, $recovery, $vtime = false) {
[32] Fix | Delete
if ($vtime === false) {
[33] Fix | Delete
$vtime = Controller_Time::time();
[34] Fix | Delete
}
[35] Fix | Delete
[36] Fix | Delete
global $wpdb;
[37] Fix | Delete
$table = Controller_DB::shared()->secrets;
[38] Fix | Delete
$wpdb->query($wpdb->prepare("INSERT INTO `{$table}` (`user_id`, `secret`, `recovery`, `ctime`, `vtime`, `mode`) VALUES (%d, %s, %s, UNIX_TIMESTAMP(), %d, 'authenticator')", $user->ID, Model_Compat::hex2bin($secret), implode('', array_map(function($r) { return Model_Compat::hex2bin($r); }, $recovery)), $vtime));
[39] Fix | Delete
}
[40] Fix | Delete
[41] Fix | Delete
/**
[42] Fix | Delete
* Validates the 2FA (or recovery) code for the given user. This will return `null` if the user does not have 2FA
[43] Fix | Delete
* enabled. This check will mark the code as used, preventing its use again.
[44] Fix | Delete
*
[45] Fix | Delete
* @param \WP_User $user
[46] Fix | Delete
* @param string $code
[47] Fix | Delete
* @return bool|null Returns null if the user does not have 2FA enabled, false if the code is invalid, and true if valid.
[48] Fix | Delete
*/
[49] Fix | Delete
public function validate_2fa($user, $code, $update = true) {
[50] Fix | Delete
global $wpdb;
[51] Fix | Delete
$table = Controller_DB::shared()->secrets;
[52] Fix | Delete
$record = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$table}` WHERE `user_id` = %d FOR UPDATE", $user->ID), ARRAY_A);
[53] Fix | Delete
if (!$record) {
[54] Fix | Delete
return null;
[55] Fix | Delete
}
[56] Fix | Delete
[57] Fix | Delete
if (preg_match('/^(?:[a-f0-9]{4}\s*){4}$/i', $code)) { //Recovery code
[58] Fix | Delete
$code = strtolower(preg_replace('/\s/i', '', $code));
[59] Fix | Delete
$recoveryCodes = str_split(strtolower(bin2hex($record['recovery'])), 16);
[60] Fix | Delete
[61] Fix | Delete
$index = array_search($code, $recoveryCodes);
[62] Fix | Delete
if ($index !== false) {
[63] Fix | Delete
if ($update) {
[64] Fix | Delete
unset($recoveryCodes[$index]);
[65] Fix | Delete
$updatedRecoveryCodes = implode('', $recoveryCodes);
[66] Fix | Delete
$wpdb->query($wpdb->prepare("UPDATE `{$table}` SET `recovery` = X%s WHERE `id` = %d", $updatedRecoveryCodes, $record['id']));
[67] Fix | Delete
}
[68] Fix | Delete
$wpdb->query('COMMIT');
[69] Fix | Delete
return true;
[70] Fix | Delete
}
[71] Fix | Delete
}
[72] Fix | Delete
else if (preg_match('/^(?:[0-9]{3}\s*){2}$/i', $code)) { //TOTP code
[73] Fix | Delete
$code = preg_replace('/\s/i', '', $code);
[74] Fix | Delete
$secret = bin2hex($record['secret']);
[75] Fix | Delete
[76] Fix | Delete
$matches = $this->check_code($secret, $code, floor($record['vtime'] / self::TIME_WINDOW_LENGTH));
[77] Fix | Delete
if ($matches !== false) {
[78] Fix | Delete
if ($update) {
[79] Fix | Delete
$wpdb->query($wpdb->prepare("UPDATE `{$table}` SET `vtime` = %d WHERE `id` = %d", $matches, $record['id']));
[80] Fix | Delete
}
[81] Fix | Delete
$wpdb->query('COMMIT');
[82] Fix | Delete
return true;
[83] Fix | Delete
}
[84] Fix | Delete
}
[85] Fix | Delete
[86] Fix | Delete
$wpdb->query('ROLLBACK');
[87] Fix | Delete
return false;
[88] Fix | Delete
}
[89] Fix | Delete
[90] Fix | Delete
/**
[91] Fix | Delete
* Checks whether or not the code is valid for the given secret. If it is, it returns the time window (as a timestamp)
[92] Fix | Delete
* that matched. If no time windows are provided, it checks the current and one on each side.
[93] Fix | Delete
*
[94] Fix | Delete
* @param string $secret The secret as a hex string.
[95] Fix | Delete
* @param string $code The code.
[96] Fix | Delete
* @param null|int The last-used time window (as a timestamp).
[97] Fix | Delete
* @param null|array $windows An array of time windows or null to use the default.
[98] Fix | Delete
* @return bool|int The time window if matches, otherwise false.
[99] Fix | Delete
*/
[100] Fix | Delete
public function check_code($secret, $code, $previous = null, $windows = null) {
[101] Fix | Delete
$timeCode = floor(Controller_Time::time() / self::TIME_WINDOW_LENGTH);
[102] Fix | Delete
[103] Fix | Delete
if ($windows === null) {
[104] Fix | Delete
$windows = array();
[105] Fix | Delete
$validRange = array(-1, 1); //90 second range for authenticator
[106] Fix | Delete
[107] Fix | Delete
$lowRange = $validRange[0];
[108] Fix | Delete
$highRange = $validRange[1];
[109] Fix | Delete
for ($i = 0; $i >= $lowRange; $i--) {
[110] Fix | Delete
$windows[] = $timeCode + $i;
[111] Fix | Delete
}
[112] Fix | Delete
for ($i = 1; $i <= $highRange; $i++) {
[113] Fix | Delete
$windows[] = $timeCode + $i;
[114] Fix | Delete
}
[115] Fix | Delete
}
[116] Fix | Delete
[117] Fix | Delete
foreach ($windows as $w) {
[118] Fix | Delete
if ($previous !== null && $previous >= $w) {
[119] Fix | Delete
continue;
[120] Fix | Delete
}
[121] Fix | Delete
[122] Fix | Delete
$expectedCode = $this->_generate_totp($secret, dechex($w));
[123] Fix | Delete
if (hash_equals($expectedCode, $code)) {
[124] Fix | Delete
return $w * self::TIME_WINDOW_LENGTH;
[125] Fix | Delete
}
[126] Fix | Delete
}
[127] Fix | Delete
[128] Fix | Delete
return false;
[129] Fix | Delete
}
[130] Fix | Delete
[131] Fix | Delete
/**
[132] Fix | Delete
* Generates a TOTP value using the provided parameters.
[133] Fix | Delete
*
[134] Fix | Delete
* @param $key The key in hex.
[135] Fix | Delete
* @param $time The desired time code in hex.
[136] Fix | Delete
* @param int $digits The number of digits.
[137] Fix | Delete
* @return string The TOTP value.
[138] Fix | Delete
*/
[139] Fix | Delete
private function _generate_totp($key, $time, $digits = 6)
[140] Fix | Delete
{
[141] Fix | Delete
$time = Model_Compat::hex2bin(str_pad($time, 16, '0', STR_PAD_LEFT));
[142] Fix | Delete
$key = Model_Compat::hex2bin($key);
[143] Fix | Delete
$hash = hash_hmac('sha1', $time, $key);
[144] Fix | Delete
[145] Fix | Delete
$offset = hexdec(substr($hash, -2)) & 0xf;
[146] Fix | Delete
$intermediate = ( ((hexdec(substr($hash, $offset * 2, 2)) & 0x7f) << 24) |
[147] Fix | Delete
((hexdec(substr($hash, ($offset + 1) * 2, 2)) & 0xff) << 16) |
[148] Fix | Delete
((hexdec(substr($hash, ($offset + 2) * 2, 2)) & 0xff) << 8) |
[149] Fix | Delete
((hexdec(substr($hash, ($offset + 3) * 2, 2)) & 0xff))
[150] Fix | Delete
);
[151] Fix | Delete
$otp = $intermediate % pow(10, $digits);
[152] Fix | Delete
[153] Fix | Delete
return str_pad("{$otp}", $digits, '0', STR_PAD_LEFT);
[154] Fix | Delete
}
[155] Fix | Delete
}
[156] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function