: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if (in_array($real, $processedDirs)) {
$processedDirs[] = $real;
while ($sub = readdir($dir)) {
if ($sub == '.' || $sub == '..') { continue; }
foreach ($contents as $f) {
$removed = self::_recursivelyRemoveWflogs($file . '/' . $f, $processedDirs);
$filesRemoved = array($filesRemoved, $removed);
public static function loginAction($username){
if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
if(! $username){ return; }
wfConfig::inc('totalLogins');
$user = get_user_by('login', $username);
$userID = $user ? $user->ID : 0;
self::getLog()->logLogin('loginOK', 0, $username);
if(wfUtils::isAdmin($user)){
wfConfig::set_ser('lastAdminLogin', array(
'firstName' => $user->first_name,
'lastName' => $user->last_name,
'time' => wfUtils::localHumanDateShort(),
$salt = wp_salt('logged_in');
//TODO: Drop support for legacy cookie after 1 year
$legacyCookieName = 'wf_loginalerted_' . hash_hmac('sha256', wfUtils::getIP() . '|' . $user->ID, $salt);
$cookieName = 'wf_loginalerted_' . hash_hmac('sha256', $user->ID, $salt);
$cookieValue = hash_hmac('sha256', $user->user_login, $salt);
$newDevice = !(isset($_COOKIE[$legacyCookieName]) && hash_equals($cookieValue, $_COOKIE[$legacyCookieName])); //Check legacy cookie
$newDevice = !(isset($_COOKIE[$cookieName]) && hash_equals($cookieValue, $_COOKIE[$cookieName]));
$_COOKIE[$cookieName]=$cookieValue;
if(wfUtils::isAdmin($userID)){
$securityEvent = 'adminLogin';
$alertCallback = array(new wfAdminLoginAlert($cookieName, $cookieValue, $username, wfUtils::getIP()), 'send');
$securityEvent = 'nonAdminLogin';
$alertCallback = array(new wfNonAdminLoginAlert($cookieName, $cookieValue, $username, wfUtils::getIP()), 'send');
$securityEvent.='NewLocation';
do_action('wordfence_security_event', $securityEvent, array(
'ip' => wfUtils::getIP(),
if (wfConfig::get(wfUtils::isAdmin($userID)?'alertOn_firstAdminLoginOnly':'alertOn_firstNonAdminLoginOnly')) {
//Purge legacy cookie if still present
if(array_key_exists($legacyCookieName, $_COOKIE))
wfUtils::setcookie($legacyCookieName, '', 1, '/', null, wfUtils::isFullSSL(), true);
wfUtils::setcookie($cookieName, $cookieValue, time() + (86400 * 365), '/', null, wfUtils::isFullSSL(), true);
public static function registrationFilter($errors, $sanitizedLogin, $userEmail) {
if (wfConfig::get('loginSec_blockAdminReg') && $sanitizedLogin == 'admin') {
$errors->add('user_login_error', __('<strong>ERROR</strong>: You can\'t register using that username', 'wordfence'));
public static function wooRegistrationFilter($wooCustomerData) {
$wooCustomerData matches:
'user_login' => $username,
'user_pass' => $password,
if (wfConfig::get('loginSec_blockAdminReg') && is_array($wooCustomerData) && isset($wooCustomerData['user_login']) && isset($wooCustomerData['user_email']) && preg_match('/^admin\d*$/i', $wooCustomerData['user_login'])) {
//Converts a username of `admin` generated from something like `admin@example.com` to `adminexample`
$emailComponents = explode('@', $wooCustomerData['user_email']);
if (strpos(wfUtils::array_last($emailComponents), '.') === false) { //e.g., admin@localhost
$wooCustomerData['user_login'] .= wfUtils::array_last($emailComponents);
else { //e.g., admin@example.com
$hostComponents = explode('.', wfUtils::array_last($emailComponents));
array_pop($hostComponents);
$wooCustomerData['user_login'] .= wfUtils::array_last($hostComponents);
//If it's still `admin` at this point, it will fall through and get blocked by wordfence::blacklistedUsernames
public static function oembedAuthorFilter($data, $post, $width, $height) {
unset($data['author_name']);
unset($data['author_url']);
public static function jsonAPIAuthorFilter($response, $handler, $request) {
$route = $request->get_route();
if (!current_user_can('edit_others_posts')) {
$urlBase = wfWP_REST_Users_Controller::wfGetURLBase();
if (preg_match('~' . preg_quote($urlBase, '~') . '/*$~i', $route)) {
$error = new WP_Error('rest_user_cannot_view', __('Sorry, you are not allowed to list users.', 'wordfence'), array('status' => rest_authorization_required_code()));
$response = rest_ensure_response($error);
if (!defined('WORDFENCE_REST_API_SUPPRESSED')) { define('WORDFENCE_REST_API_SUPPRESSED', true); }
else if (preg_match('~' . preg_quote($urlBase, '~') . '/+(\d+)/*$~i', $route, $matches)) {
if (get_current_user_id() !== $id) {
$error = new WP_Error('rest_user_invalid_id', __('Invalid user ID.', 'wordfence'), array('status' => 404));
$response = rest_ensure_response($error);
if (!defined('WORDFENCE_REST_API_SUPPRESSED')) { define('WORDFENCE_REST_API_SUPPRESSED', true); }
public static function jsonAPIAdjustHeaders($response, $server, $request) {
if (defined('WORDFENCE_REST_API_SUPPRESSED')) {
$response->header('Allow', 'GET');
public static function wpSitemapUserProviderFilter($provider, $name) {
public static function _filterCentralFromLiveTraffic($dispatch_result, $request, $route, $handler) {
if (preg_match('~^/wordfence/v\d+/~i', $route)) {
self::getLog()->canLogHit = false;
public static function showTwoFactorField() {
$existingContents = ob_get_contents();
if (!preg_match('/wftwofactornonce:([0-9]+)\/(.+?)\s/', $existingContents, $matches)) {
$userID = intval($matches[1]);
$twoFactorNonce = preg_replace('/[^a-f0-9]/i', '', $matches[2]);
if (!self::verifyTwoFactorIntermediateValues($userID, $twoFactorNonce)) {
//Strip out the username and password fields
$formPosition = strrpos($existingContents, '<form');
$formTagEnd = strpos($existingContents, '>', $formPosition);
if ($formPosition === false || $formTagEnd === false) {
echo substr($existingContents, 0, $formTagEnd + 1);
<label for=\"wfAuthenticationCode\">Authentication Code<br>
<input type=\"text\" size=\"6\" class=\"input\" id=\"wordfence_authFactor\" name=\"wordfence_authFactor\" autofocus></label>
<input type=\"hidden\" id=\"wordfence_twoFactorUser\" name=\"wordfence_twoFactorUser\" value=\"" . $userID . "\">
<input type=\"hidden\" id=\"wordfence_twoFactorNonce\" name=\"wordfence_twoFactorNonce\" value=\"" . $twoFactorNonce . "\">
private static function verifyTwoFactorIntermediateValues($userID, $twoFactorNonce) {
$user = get_user_by('ID', $userID);
if (!$user || get_class($user) != 'WP_User') { return false; } //Check that the user exists
$expectedNonce = get_user_meta($user->ID, '_wf_twoFactorNonce', true);
$twoFactorNonceTime = get_user_meta($user->ID, '_wf_twoFactorNonceTime', true);
if (empty($twoFactorNonce) || empty($twoFactorNonceTime)) { return false; } //Ensure the two factor nonce and time have been set
if ($twoFactorNonce != $expectedNonce) { return false; } //Verify the nonce matches the expected
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
if (!$twoFactorUsers || !is_array($twoFactorUsers)) { return false; } //Make sure there are two factor users configured
foreach ($twoFactorUsers as &$t) { //Ensure the two factor nonce hasn't expired
if ($t[0] == $user->ID && $t[3] == 'activated') {
if (isset($t[5]) && $t[5] == 'authenticator') { $graceTime = WORDFENCE_TWO_FACTOR_GRACE_TIME_AUTHENTICATOR; }
else { $graceTime = WORDFENCE_TWO_FACTOR_GRACE_TIME_PHONE; }
return ((time() - $twoFactorNonceTime) < $graceTime);
public static function authenticateFilter($authUser, $username, $passwd) {
wfConfig::inc('totalLoginHits'); //The total hits to wp-login.php including logins, logouts and just hits.
$secEnabled = wfConfig::get('loginSecurityEnabled');
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
$userDat = self::$userDat;
$checkBreachList = $secEnabled &&
!wfBlock::isWhitelisted($IP) &&
wfConfig::get('loginSec_breachPasswds_enabled') &&
get_class($authUser) == 'WP_User' &&
((wfConfig::get('loginSec_breachPasswds') == 'admins' && wfUtils::isAdmin($authUser)) || (wfConfig::get('loginSec_breachPasswds') == 'pubs' && user_can($authUser, 'publish_posts')));
$usingBreachedPassword = false;
$cacheStatus = wfCredentialsController::cachedCredentialStatus($authUser);
if ($cacheStatus != wfCredentialsController::UNCACHED) {
$usingBreachedPassword = ($cacheStatus == wfCredentialsController::LEAKED);
if (wfCredentialsController::isLeakedPassword($authUser->username, $passwd)) {
$usingBreachedPassword = true;
wfCredentialsController::setCachedCredentialStatus($authUser, $usingBreachedPassword);
$checkTwoFactor = $secEnabled &&
!wfBlock::isWhitelisted($IP) &&
wfConfig::get('isPaid') &&
isset($twoFactorUsers) &&
is_array($twoFactorUsers) &&
sizeof($twoFactorUsers) > 0 &&
get_class($userDat) == 'WP_User' &&
wfCredentialsController::useLegacy2FA();
$twoFactorRecord = false;
$hasActivatedTwoFactorUser = false;
foreach ($twoFactorUsers as &$t) {
if ($t[3] == 'activated') {
$testUser = get_user_by('ID', $userID);
if (is_object($testUser) && wfUtils::isAdmin($testUser)) {
$hasActivatedTwoFactorUser = true;
if ($userID == $userDat->ID) {
if (isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor'] && $twoFactorRecord) { //User authenticated with name and password, 2FA code ready to check
if (is_object($authUser) && 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)) {
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_required', wp_kses(__('<strong>VERIFICATION FAILED</strong>: Two-factor authentication verification failed. Please try again.', 'wordfence'), array('strong'=>array())));
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(/* translators: 1. WordPress username. 2. Password reset URL. */ wp_kses(__('<strong>ERROR</strong>: The username or password you entered is incorrect. <a href="%2$s" title="Password Lost and Found">Lost your password</a>?', 'wordfence'), array('strong'=>array(), 'a'=>array('href'=>array(), 'title'=>array()))), $username, wp_lostpassword_url()));
return self::processBruteForceAttempt(self::$authError, $username, $passwd);
if ($usingBreachedPassword) {
wfAdminNoticeQueue::removeAdminNotice(false, 'previousIPBreachPassword', array($userID));
wfAdminNoticeQueue::addAdminNotice(wfAdminNotice::SEVERITY_CRITICAL, sprintf(
/* translators: 1. WordPress admin panel URL. 2. Support URL. */
__('<strong>WARNING: </strong>The password you are using exists on lists of passwords leaked in data breaches. Attackers use such lists to break into sites and install malicious code. Please <a href="%1$s">change your password</a>. <a href="%2$s" target="_blank" rel="noopener noreferrer">Learn More<span class="screen-reader-text"> (' . esc_html__('opens in new tab', 'wordfence') . ')</span></a>', 'wordfence'),
self_admin_url('profile.php'),
wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD)
), '2faBreachPassword', array($authUser->ID));
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());
$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
if (is_object($authUser) && get_class($authUser) == 'WP_User' && $authUser->ID == $userID) { //Using the old method of appending the code to the password
if ($mode == 'authenticator') {
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_invalid', wp_kses(__('<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>).', 'wordfence'), array('strong'=>array(), 'code'=>array())));
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_invalid', wp_kses(__('<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>).', 'wordfence'), array('strong'=>array(), 'code'=>array())));
$loginNonce = wfWAFUtils::random_bytes(20);
if ($loginNonce === false) { //Should never happen but is technically possible
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_required', wp_kses(__('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.', 'wordfence'), array('strong'=>array())));
$loginNonce = bin2hex($loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
if ($mode == 'authenticator') {
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_invalid', wp_kses(__('<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).', 'wordfence'), array('strong'=>array())) . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_invalid', wp_kses(__('<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).', 'wordfence'), array('strong'=>array())) . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
return self::processBruteForceAttempt(self::$authError, $username, $passwd);
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());
$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
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_required', wp_kses(__('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.', 'wordfence'), array('strong'=>array())));
$loginNonce = bin2hex($loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_required', wp_kses(__('<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.', 'wordfence'), array('strong'=>array())) . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
//else: No new code was received. Let them sign in with the expired code.
// 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
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_required', wp_kses(__('<strong>AUTHENTICATION FAILURE</strong>: A temporary failure was encountered while trying to log in. Please try again.', 'wordfence'), array('strong'=>array())));
$loginNonce = bin2hex($loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_invalid', wp_kses(__('<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).', 'wordfence'), array('strong'=>array())) . '<!-- 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 (is_object($authUser) && 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[0] == $userDat->ID && $twoFactorRecord[3] == 'activated') { //Yup, enabled, so require the code
if ($usingBreachedPassword) {
wfAdminNoticeQueue::removeAdminNotice(false, 'previousIPBreachPassword', array($authUser->ID));
wfAdminNoticeQueue::addAdminNotice(wfAdminNotice::SEVERITY_CRITICAL, sprintf(
/* translators: 1. WordPress admin panel URL. 2. Support URL. */
__('<strong>WARNING: </strong>The password you are using exists on lists of passwords leaked in data breaches. Attackers use such lists to break into sites and install malicious code. Please <a href="%1$s">change your password</a>. <a href="%2$s" target="_blank" rel="noopener noreferrer">Learn More<span class="screen-reader-text"> (' . esc_html__('opens in new tab', 'wordfence') . ')</span></a>', 'wordfence'), self_admin_url('profile.php'), wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD)), '2faBreachPassword', array($authUser->ID));
$loginNonce = wfWAFUtils::random_bytes(20);
if ($loginNonce === false) { //Should never happen but is technically possible, allow login
$requireAdminTwoFactor = false;
$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)) {
if (isset($retries[$ip]) && is_int($retries[$ip])) {
update_option('limit_login_retries', $retries);
$allowSeparatePrompt = ini_get('output_buffering') > 0;
if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_required', wp_kses(__('<strong>CODE REQUIRED</strong>: Please check your authenticator app for the current code. Enter it below to sign in.', 'wordfence'), array('strong'=>array())) . '<!-- wftwofactornonce:' . $userDat->ID . '/' . $loginNonce . ' -->');
remove_action('login_errors', 'limit_login_fixup_error_messages'); //We're forced to do this because limit-login-attempts does not have any allowances for legitimate error messages
self::$authError = new WP_Error('twofactor_required', wp_kses(__('<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>).', 'wordfence'), array('strong'=>array(), 'code'=>array())));
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
$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.
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)) {
if (isset($retries[$ip]) && is_int($retries[$ip])) {