Пример #1
0
 public static function authenticateFilter($authUser, $username, $passwd)
 {
     wfConfig::inc('totalLoginHits');
     //The total hits to wp-login.php including logins, logouts and just hits.
     $IP = wfUtils::getIP();
     $secEnabled = wfConfig::get('loginSecurityEnabled');
     $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
     $userDat = isset($_POST['wordfence_userDat']) ? $_POST['wordfence_userDat'] : false;
     $checkTwoFactor = $secEnabled && !self::getLog()->isWhitelisted($IP) && wfConfig::get('isPaid') && isset($twoFactorUsers) && is_array($twoFactorUsers) && sizeof($twoFactorUsers) > 0 && is_object($userDat) && get_class($userDat) == 'WP_User';
     if ($checkTwoFactor) {
         $twoFactorRecord = false;
         $hasActivatedTwoFactorUser = false;
         foreach ($twoFactorUsers as &$t) {
             if ($t[3] == 'activated') {
                 $userID = $t[0];
                 $testUser = get_user_by('ID', $userID);
                 if (is_object($testUser) && wfUtils::isAdmin($testUser)) {
                     $hasActivatedTwoFactorUser = true;
                 }
                 if ($userID == $userDat->ID) {
                     $twoFactorRecord =& $t;
                 }
             }
         }
         if (isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor'] && $twoFactorRecord) {
             //User authenticated with name and password, 2FA code ready to check
             $userID = $userDat->ID;
             if (get_class($authUser) == 'WP_User' && $authUser->ID == $userID) {
                 //Do nothing. This is the code path the old method of including the code in the password field will take -- since we already have a valid $authUser, skip the nonce verification portion
             } else {
                 if (isset($_POST['wordfence_twoFactorNonce'])) {
                     $twoFactorNonce = preg_replace('/[^a-f0-9]/i', '', $_POST['wordfence_twoFactorNonce']);
                     if (!self::verifyTwoFactorIntermediateValues($userID, $twoFactorNonce)) {
                         self::$authError = new WP_Error('twofactor_required', __('<strong>VERIFICATION FAILED</strong>: Two factor authentication verification failed. Please try again.'));
                         return self::processBruteForceAttempt(self::$authError, $username, $passwd);
                     }
                 } else {
                     //Code path for old method, invalid password the second time
                     self::$authError = $authUser;
                     if (is_wp_error(self::$authError) && (self::$authError->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'invalid_email' || self::$authError->get_error_code() == 'incorrect_password' || $authUser->get_error_code() == 'authentication_failed') && wfConfig::get('loginSec_maskLoginErrors')) {
                         self::$authError = new WP_Error('incorrect_password', sprintf(__('<strong>ERROR</strong>: The username or password you entered is incorrect. <a href="%2$s" title="Password Lost and Found">Lost your password</a>?'), $username, wp_lostpassword_url()));
                     }
                     return self::processBruteForceAttempt(self::$authError, $username, $passwd);
                 }
             }
             if (isset($twoFactorRecord[5])) {
                 //New method TOTP
                 $mode = $twoFactorRecord[5];
                 $code = preg_replace('/[^a-f0-9]/i', '', $_POST['wordfence_authFactor']);
                 $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                 try {
                     $codeResult = $api->call('twoFactorTOTP_verify', array(), array('totpid' => $twoFactorRecord[6], 'code' => $code, 'mode' => $mode));
                     if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                         //No longer a paid key, let them sign in without two factor
                     } else {
                         if (isset($codeResult['ok']) && $codeResult['ok']) {
                             //Everything's good, let the sign in continue
                         } else {
                             if (get_class($authUser) == 'WP_User' && $authUser->ID == $userID) {
                                 //Using the old method of appending the code to the password
                                 if ($mode == 'authenticator') {
                                     self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: Please sign in again and add a space, the letters <code>wf</code>, and the code from your authenticator app to the end of your password (e.g., <code>wf123456</code>).'));
                                 } else {
                                     self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: Please sign in again and add a space, the letters <code>wf</code>, and the code sent to your phone to the end of your password (e.g., <code>wf123456</code>).'));
                                 }
                             } else {
                                 $loginNonce = wfWAFUtils::random_bytes(20);
                                 if ($loginNonce === false) {
                                     //Should never happen but is technically possible
                                     self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.'));
                                     return self::$authError;
                                 }
                                 $loginNonce = bin2hex($loginNonce);
                                 update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
                                 update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
                                 if ($mode == 'authenticator') {
                                     self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter the code generated by your authenticator app. The code should be a six digit number (e.g., 123456).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                 } else {
                                     self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter the code generated sent to your phone. The code should be a six digit number (e.g., 123456).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                 }
                             }
                             return self::processBruteForceAttempt(self::$authError, $username, $passwd);
                         }
                     }
                 } catch (Exception $e) {
                     if (self::isDebugOn()) {
                         error_log('TOTP validation error: ' . $e->getMessage());
                     }
                 }
                 // Couldn't connect to noc1, let them sign in since the password was correct.
             } else {
                 //Old method phone authentication
                 $authFactor = $_POST['wordfence_authFactor'];
                 if (strlen($authFactor) == 4) {
                     $authFactor = 'wf' . $authFactor;
                 }
                 if ($authFactor == $twoFactorRecord[2] && $twoFactorRecord[4] > time()) {
                     // Set this 2FA code to expire in 30 seconds (for other plugins hooking into the auth process)
                     $twoFactorRecord[4] = time() + 30;
                     wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                 } else {
                     if ($authFactor == $twoFactorRecord[2]) {
                         $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                         try {
                             $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $twoFactorRecord[1]));
                             if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                                 //No longer a paid key, let them sign in without two factor
                             } else {
                                 if (isset($codeResult['ok']) && $codeResult['ok']) {
                                     $twoFactorRecord[2] = $codeResult['code'];
                                     $twoFactorRecord[4] = time() + 1800;
                                     //30 minutes until code expires
                                     wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                                     //save the code the user needs to enter and return an error.
                                     $loginNonce = wfWAFUtils::random_bytes(20);
                                     if ($loginNonce === false) {
                                         //Should never happen but is technically possible
                                         self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.'));
                                         return self::$authError;
                                     }
                                     $loginNonce = bin2hex($loginNonce);
                                     update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
                                     update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
                                     self::$authError = new WP_Error('twofactor_required', __('<strong>CODE EXPIRED. CHECK YOUR PHONE:</strong> The code you entered has expired. Codes are only valid for 30 minutes for security reasons. We have sent you a new code. Please sign in using your username, password, and the new code we sent you.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                     return self::$authError;
                                 }
                             }
                             //else: No new code was received. Let them sign in with the expired code.
                         } catch (Exception $e) {
                             // Couldn't connect to noc1, let them sign in since the password was correct.
                         }
                     } else {
                         //Bad code, so cancel the login and return an error to user.
                         $loginNonce = wfWAFUtils::random_bytes(20);
                         if ($loginNonce === false) {
                             //Should never happen but is technically possible
                             self::$authError = new WP_Error('twofactor_required', __('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.'));
                             return self::$authError;
                         }
                         $loginNonce = bin2hex($loginNonce);
                         update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
                         update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
                         self::$authError = new WP_Error('twofactor_invalid', __('<strong>INVALID CODE</strong>: You need to enter your password and the code we sent to your phone. The code should start with \'wf\' and should be four characters (e.g., wfAB12).') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                         return self::processBruteForceAttempt(self::$authError, $username, $passwd);
                     }
                 }
             }
             delete_user_meta($userDat->ID, '_wf_twoFactorNonce');
             delete_user_meta($userDat->ID, '_wf_twoFactorNonceTime');
             $authUser = $userDat;
             //Log in as the user we saved in the wp_authenticate action
         } else {
             if (get_class($authUser) == 'WP_User') {
                 //User authenticated with name and password, prompt for the 2FA code
                 //Verify at least one administrator has 2FA enabled
                 $requireAdminTwoFactor = $hasActivatedTwoFactorUser && wfConfig::get('loginSec_requireAdminTwoFactor');
                 if ($twoFactorRecord) {
                     if ($twoFactorRecord[0] == $userDat->ID && $twoFactorRecord[3] == 'activated') {
                         //Yup, enabled, so require the code
                         $loginNonce = wfWAFUtils::random_bytes(20);
                         if ($loginNonce === false) {
                             //Should never happen but is technically possible, allow login
                             $requireAdminTwoFactor = false;
                         } else {
                             $loginNonce = bin2hex($loginNonce);
                             update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
                             update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
                             if (isset($twoFactorRecord[5])) {
                                 //New method TOTP authentication
                                 if ($twoFactorRecord[5] == 'authenticator') {
                                     if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
                                         $retries = get_option('limit_login_retries', array());
                                         $ip = limit_login_get_address();
                                         if (!is_array($retries)) {
                                             $retries = array();
                                         }
                                         if (isset($retries[$ip]) && is_int($retries[$ip])) {
                                             $retries[$ip]--;
                                         } else {
                                             $retries[$ip] = 0;
                                         }
                                         update_option('limit_login_retries', $retries);
                                     }
                                     $allowSeparatePrompt = ini_get('output_buffering') > 0;
                                     if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
                                         self::$authError = new WP_Error('twofactor_required', __('<strong>CODE REQUIRED</strong>: Please check your authenticator app for the current code. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                         return self::$authError;
                                     } else {
                                         self::$authError = new WP_Error('twofactor_required', __('<strong>CODE REQUIRED</strong>: Please check your authenticator app for the current code. Please sign in again and add a space, the letters <code>wf</code>, and the code to the end of your password (e.g., <code>wf123456</code>).'));
                                         return self::$authError;
                                     }
                                 } else {
                                     //Phone TOTP
                                     $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                                     try {
                                         $codeResult = $api->call('twoFactorTOTP_sms', array(), array('totpid' => $twoFactorRecord[6]));
                                         if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                                             $requireAdminTwoFactor = false;
                                             //Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up.
                                         } else {
                                             if (isset($codeResult['ok']) && $codeResult['ok']) {
                                                 if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
                                                     $retries = get_option('limit_login_retries', array());
                                                     $ip = limit_login_get_address();
                                                     if (!is_array($retries)) {
                                                         $retries = array();
                                                     }
                                                     if (isset($retries[$ip]) && is_int($retries[$ip])) {
                                                         $retries[$ip]--;
                                                     } else {
                                                         $retries[$ip] = 0;
                                                     }
                                                     update_option('limit_login_retries', $retries);
                                                 }
                                                 $allowSeparatePrompt = ini_get('output_buffering') > 0;
                                                 if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
                                                     self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                                     return self::$authError;
                                                 } else {
                                                     self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space, the letters <code>wf</code>, and the code to the end of your password (e.g., <code>wf123456</code>).'));
                                                     return self::$authError;
                                                 }
                                             } else {
                                                 //oops, our API returned an error.
                                                 $requireAdminTwoFactor = false;
                                                 //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
                                             }
                                         }
                                     } catch (Exception $e) {
                                         if (self::isDebugOn()) {
                                             error_log('TOTP SMS error: ' . $e->getMessage());
                                         }
                                         $requireAdminTwoFactor = false;
                                         // Couldn't connect to noc1, let them sign in since the password was correct.
                                     }
                                 }
                             } else {
                                 //Old method phone authentication
                                 $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
                                 try {
                                     $codeResult = $api->call('twoFactor_verification', array(), array('phone' => $twoFactorRecord[1]));
                                     if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
                                         $requireAdminTwoFactor = false;
                                         //Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up.
                                     } else {
                                         if (isset($codeResult['ok']) && $codeResult['ok']) {
                                             $twoFactorRecord[2] = $codeResult['code'];
                                             $twoFactorRecord[4] = time() + 1800;
                                             //30 minutes until code expires
                                             wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
                                             //save the code the user needs to enter and return an error.
                                             if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
                                                 $retries = get_option('limit_login_retries', array());
                                                 $ip = limit_login_get_address();
                                                 if (!is_array($retries)) {
                                                     $retries = array();
                                                 }
                                                 if (isset($retries[$ip]) && is_int($retries[$ip])) {
                                                     $retries[$ip]--;
                                                 } else {
                                                     $retries[$ip] = 0;
                                                 }
                                                 update_option('limit_login_retries', $retries);
                                             }
                                             $allowSeparatePrompt = ini_get('output_buffering') > 0;
                                             if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
                                                 self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Enter it below to sign in.') . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
                                                 return self::$authError;
                                             } else {
                                                 self::$authError = new WP_Error('twofactor_required', __('<strong>CHECK YOUR PHONE</strong>: A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space and the code to the end of your password (e.g., <code>wfABCD</code>).'));
                                                 return self::$authError;
                                             }
                                         } else {
                                             //oops, our API returned an error.
                                             $requireAdminTwoFactor = false;
                                             //Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
                                         }
                                     }
                                 } catch (Exception $e) {
                                     $requireAdminTwoFactor = false;
                                     // Couldn't connect to noc1, let them sign in since the password was correct.
                                 }
                             }
                             //end: Old method phone authentication
                         }
                     }
                 }
                 if ($requireAdminTwoFactor && wfUtils::isAdmin($authUser)) {
                     $username = $authUser->user_login;
                     self::getLog()->logLogin('loginFailValidUsername', 1, $username);
                     wordfence::alert("Admin Login Blocked", "A user with username \"{$username}\" who has administrator access tried to sign in to your WordPress site. Access was denied because all administrator accounts are required to have Cellphone Sign-in enabled but this account does not.", wfUtils::getIP());
                     self::$authError = new WP_Error('twofactor_disabled_required', __('<strong>Cellphone Sign-in Required</strong>: Cellphone Sign-in is required for all administrator accounts. Please contact the site administrator to enable it for your account.'));
                     return self::$authError;
                 }
                 //User is not configured for two factor. Sign in without two factor.
             }
         }
     }
     //End: if ($checkTwoFactor)
     return self::processBruteForceAttempt($authUser, $username, $passwd);
 }
Пример #2
0
 public static function requestDetectProxyCallback($timeout = 0.01, $blocking = false)
 {
     $nonce = bin2hex(wfWAFUtils::random_bytes(32));
     $callback = self::getSiteBaseURL() . '?_wfsf=detectProxy';
     wfConfig::set('detectProxyNonce', $nonce, wfConfig::DONT_AUTOLOAD);
     wfConfig::set('detectProxyRecommendation', '', wfConfig::DONT_AUTOLOAD);
     $payload = array('nonce' => $nonce, 'callback' => $callback);
     $siteurl = '';
     if (function_exists('get_bloginfo')) {
         if (is_multisite()) {
             $siteurl = network_home_url();
             $siteurl = rtrim($siteurl, '/');
             //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
         } else {
             $siteurl = home_url();
         }
     }
     wp_remote_post(WFWAF_API_URL_SEC . "?" . http_build_query(array('action' => 'detect_proxy', 'k' => wfConfig::get('apiKey'), 's' => $siteurl, 't' => microtime(true)), null, '&'), array('body' => json_encode($payload), 'headers' => array('Content-Type' => 'application/json'), 'timeout' => $timeout, 'blocking' => $blocking));
     //Asynchronous so we don't care about a response at this point.
 }