require_once(dirname(__FILE__) . '/wordfenceConstants.php'); require_once(dirname(__FILE__) . '/wfScanEngine.php'); require_once(dirname(__FILE__) . '/wfScan.php'); require_once(dirname(__FILE__) . '/wfScanMonitor.php'); require_once(dirname(__FILE__) . '/wfCrawl.php'); require_once(dirname(__FILE__) . '/Diff.php'); require_once(dirname(__FILE__) . '/Diff/Renderer/Html/SideBySide.php'); require_once(dirname(__FILE__) . '/wfAPI.php'); require_once(dirname(__FILE__) . '/wfIssues.php'); require_once(dirname(__FILE__) . '/wfDB.php'); require_once(dirname(__FILE__) . '/wfUtils.php'); require_once(dirname(__FILE__) . '/wfLog.php'); require_once(dirname(__FILE__) . '/wfConfig.php'); require_once(dirname(__FILE__) . '/wfSchema.php'); require_once(dirname(__FILE__) . '/wfCache.php'); require_once(dirname(__FILE__) . '/wfCrypt.php'); require_once(dirname(__FILE__) . '/wfMD5BloomFilter.php'); require_once(dirname(__FILE__) . '/wfView.php'); require_once(dirname(__FILE__) . '/wfHelperString.php'); require_once(dirname(__FILE__) . '/wfDirectoryIterator.php'); require_once(dirname(__FILE__) . '/wfUpdateCheck.php'); require_once(dirname(__FILE__) . '/wfActivityReport.php'); require_once(dirname(__FILE__) . '/wfHelperBin.php'); require_once(dirname(__FILE__) . '/wfDiagnostic.php'); require_once(dirname(__FILE__) . '/wfStyle.php'); require_once(dirname(__FILE__) . '/wfDashboard.php'); require_once(dirname(__FILE__) . '/wfAuditLog.php'); require_once(dirname(__FILE__) . '/wfNotification.php'); require_once(dirname(__FILE__) . '/../models/page/wfPage.php'); require_once(dirname(__FILE__) . '/../models/common/wfTab.php'); require_once(dirname(__FILE__) . '/../models/block/wfBlock.php'); require_once(dirname(__FILE__) . '/../models/block/wfRateLimit.php'); require_once(dirname(__FILE__) . '/../models/firewall/wfFirewall.php'); require_once(dirname(__FILE__) . '/../models/scanner/wfScanner.php'); require_once(dirname(__FILE__) . '/wfPersistenceController.php'); require_once(dirname(__FILE__) . '/wfImportExportController.php'); require_once(dirname(__FILE__) . '/wfOnboardingController.php'); require_once(dirname(__FILE__) . '/wfSupportController.php'); require_once(dirname(__FILE__) . '/wfCredentialsController.php'); require_once(dirname(__FILE__) . '/wfVersionCheckController.php'); require_once(dirname(__FILE__) . '/wfDateLocalization.php'); require_once(dirname(__FILE__) . '/wfAdminNoticeQueue.php'); require_once(dirname(__FILE__) . '/wfModuleController.php'); require_once(dirname(__FILE__) . '/wfAlerts.php'); require_once(dirname(__FILE__) . '/wfDeactivationOption.php'); if (version_compare(phpversion(), '5.3', '>=')) { require_once(dirname(__FILE__) . '/WFLSPHP52Compatability.php'); define('WORDFENCE_USE_LEGACY_2FA', wfCredentialsController::useLegacy2FA()); $wfCoreLoading = true; require(dirname(__FILE__) . '/../modules/login-security/wordfence-login-security.php'); } require_once(dirname(__FILE__) . '/wfJWT.php'); require_once(dirname(__FILE__) . '/wfCentralAPI.php'); if (class_exists('WP_REST_Users_Controller')) { //WP 4.7+ require_once(dirname(__FILE__) . '/wfRESTAPI.php'); } if (wfCentral::isSupported()) { //WP 4.4.0+ require_once(dirname(__FILE__) . '/rest-api/wfRESTAuthenticationController.php'); require_once(dirname(__FILE__) . '/rest-api/wfRESTConfigController.php'); require_once(dirname(__FILE__) . '/rest-api/wfRESTScanController.php'); } class wordfence { public static $printStatus = false; public static $wordfence_wp_version = false; /** * @var WP_Error */ public static $authError; private static $passwordCodePattern = '/\s+wf([a-z0-9 ]+)$/i'; protected static $lastURLError = false; protected static $curlContent = ""; protected static $curlDataWritten = 0; protected static $hasher = ''; protected static $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; protected static $ignoreList = false; private static $wfLog = false; private static $hitID = 0; private static $debugOn = null; private static $runInstallCalled = false; private static $userDat = false; const ATTACK_DATA_BODY_LIMIT=41943040; //40MB public static function installPlugin(){ self::runInstall(); if (get_current_user_id() > 0) { wfConfig::set('activatingIP', wfUtils::getIP()); } //Used by MU code below update_option('wordfenceActivated', 1); if (defined('WORDFENCE_LS_FROM_CORE') && WORDFENCE_LS_FROM_CORE) { WFLSPHP52Compatability::install_plugin(); } } public static function uninstallPlugin(){ if (!defined('WORDFENCE_DEACTIVATING')) { define('WORDFENCE_DEACTIVATING', true); } //Send admin alert $currentUser = wp_get_current_user(); $username = $currentUser->user_login; $alertCallback = array(new wfWordfenceDeactivatedAlert($username, wfUtils::getIP()), 'send'); do_action('wordfence_security_event', 'wordfenceDeactivated', array( 'username' => $username, 'ip' => wfUtils::getIP(), ), $alertCallback); //Send all pending audit events (up to the limit but disallow sending any further to avoid timeouts/orphaned crons) wfAuditLog::sendPendingAuditEvents(100, false); //Check if caching is enabled and if it is, disable it and fix the .htaccess file. wfCache::removeCaching(); //Used by MU code below update_option('wordfenceActivated', 0); wp_clear_scheduled_hook('wordfence_daily_cron'); wp_clear_scheduled_hook('wordfence_hourly_cron'); wp_clear_scheduled_hook('wordfence_daily_autoUpdate'); //Remove old legacy cron job if it exists wp_clear_scheduled_hook('wordfence_scheduled_scan'); //Remove all scheduled scans. wfScanner::shared()->unscheduleAllScans(); wfScanMonitor::handleDeactivation(); // Remove cron for email summary wfActivityReport::clearCronJobs(); // Remove the admin user list so it can be regenerated if Wordfence is reactivated. wfConfig::set_ser('adminUserList', false); if (!WFWAF_SUBDIRECTORY_INSTALL) { wfWAFConfig::set('wafDisabled', true); } if(wfConfig::get('deleteTablesOnDeact')){ if (wfCentral::isSupported() && wfCentral::isConnected()) { self::ajax_wfcentral_disconnect_callback(); } wfConfig::updateTableExists(false); $schema = new wfSchema(); $schema->dropAll(); foreach(array('wordfence_version', 'wordfenceActivated', wfSchema::TABLE_CASE_OPTION) as $opt) { if (is_multisite() && function_exists('delete_network_option')) { delete_network_option(null, $opt); } delete_option($opt); } if (!WFWAF_SUBDIRECTORY_INSTALL) { try { if (WFWAF_AUTO_PREPEND) { $helper = new wfWAFAutoPrependHelper(); if ($helper->uninstall()) { wfWAF::getInstance()->uninstall(); } } else { wfWAF::getInstance()->uninstall(); } } catch (wfWAFStorageFileException $e) { error_log($e->getMessage()); } catch (wfWAFStorageEngineMySQLiException $e) { error_log($e->getMessage()); } } } if (defined('WORDFENCE_LS_FROM_CORE') && WORDFENCE_LS_FROM_CORE) { WFLSPHP52Compatability::uninstall_plugin(); } } public static function hourlyCron() { wfLog::trimHumanCache(); wfRateLimit::trimData(); wfCentral::checkForUnsentSecurityEvents(); wfAuditLog::checkForUnsentAuditEvents(); wfCentral::populateCentralSiteData(); wfVersionCheckController::shared()->checkVersionsAndWarn(); if (wfScanner::shared()->shouldRunQuickScan()) { wfScanner::shared()->recordLastQuickScanTime(); wfScanEngine::startScan(false, wfScanner::SCAN_TYPE_QUICK); } } private static function keyAlert($msg){ self::alert($msg, $msg . " " . __("To ensure uninterrupted Premium Wordfence protection on your site,\nplease renew your license by visiting https://www.wordfence.com/ Sign in, go to your dashboard,\nselect the license about to expire and click the button to renew that license.", 'wordfence'), false); } private static function pingApiKey() { $apiKey = wfConfig::get('apiKey'); if (empty($apiKey)) return; $api = new wfAPI($apiKey, wfUtils::getWPVersion()); try { $keyType = wfLicense::KEY_TYPE_FREE; $keyData = $api->call('ping_api_key', array(), array('supportHash' => wfConfig::get('supportHash', ''), 'whitelistHash' => wfConfig::get('whitelistHash', ''), 'tldlistHash' => wfConfig::get('tldlistHash', ''), 'ipResolutionListHash' => wfConfig::get('ipResolutionListHash', ''))); if (isset($keyData['_isPaidKey'])) { $keyType = wfConfig::get('keyType'); } if (isset($keyData['_feedbackBasis'])) { wfConfig::setBool('satisfactionPromptOverride', $keyData['_feedbackBasis'] > WORDFENCE_FEEDBACK_EPOCH); } if(isset($keyData['_isPaidKey']) && $keyData['_isPaidKey']){ $keyExpDays = $keyData['_keyExpDays']; $keyIsExpired = $keyData['_expired']; if (!empty($keyData['_autoRenew'])) { if ($keyExpDays > 12) { wfConfig::set('keyAutoRenew10Sent', ''); } else if ($keyExpDays <= 12 && $keyExpDays > 0 && !wfConfig::get('keyAutoRenew10Sent')) { wfConfig::set('keyAutoRenew10Sent', 1); $email = __("Your Premium Wordfence License is set to auto-renew in 10 days.", 'wordfence'); self::alert($email, $email . " " . __("To update your license settings please visit https://www.wordfence.com/zz9/dashboard", 'wordfence'), false); } } else { if($keyExpDays > 15){ wfConfig::set('keyExp15Sent', ''); wfConfig::set('keyExp7Sent', ''); wfConfig::set('keyExp2Sent', ''); wfConfig::set('keyExp1Sent', ''); wfConfig::set('keyExpFinalSent', ''); } else if($keyExpDays <= 15 && $keyExpDays > 0){ if($keyExpDays <= 15 && $keyExpDays >= 11 && (! wfConfig::get('keyExp15Sent'))){ wfConfig::set('keyExp15Sent', 1); self::keyAlert(__("Your Premium Wordfence License expires in less than 2 weeks.", 'wordfence')); } else if($keyExpDays <= 7 && $keyExpDays >= 4 && (! wfConfig::get('keyExp7Sent'))){ wfConfig::set('keyExp7Sent', 1); self::keyAlert(__("Your Premium Wordfence License expires in less than a week.", 'wordfence')); } else if($keyExpDays == 2 && (! wfConfig::get('keyExp2Sent'))){ wfConfig::set('keyExp2Sent', 1); self::keyAlert(__("Your Premium Wordfence License expires in 2 days.", 'wordfence')); } else if($keyExpDays == 1 && (! wfConfig::get('keyExp1Sent'))){ wfConfig::set('keyExp1Sent', 1); self::keyAlert(__("Your Premium Wordfence License expires in 1 day.", 'wordfence')); } } else if($keyIsExpired && (! wfConfig::get('keyExpFinalSent')) ){ wfConfig::set('keyExpFinalSent', 1); self::keyAlert(__("Your Wordfence Premium License has Expired!", 'wordfence')); } } } if (isset($keyData['dashboard'])) { wfConfig::set('lastDashboardCheck', time()); wfDashboard::processDashboardResponse($keyData['dashboard']); } if (isset($keyData['support']) && isset($keyData['supportHash'])) { wfConfig::set('supportContent', $keyData['support'], wfConfig::DONT_AUTOLOAD); wfConfig::set('supportHash', $keyData['supportHash']); } if (isset($keyData['_whitelist']) && isset($keyData['_whitelistHash'])) { wfConfig::setJSON('whitelistPresets', $keyData['_whitelist']); wfConfig::set('whitelistHash', $keyData['_whitelistHash']); } if (isset($keyData['_tldlist']) && isset($keyData['_tldlistHash'])) { wfConfig::set('tldlist', $keyData['_tldlist'], wfConfig::DONT_AUTOLOAD); wfConfig::set('tldlistHash', $keyData['_tldlistHash']); } if (isset($keyData['_ipResolutionList']) && isset($keyData['_ipResolutionListHash'])) { wfConfig::setJSON('ipResolutionList', $keyData['_ipResolutionList']); wfConfig::set('ipResolutionListHash', $keyData['_ipResolutionListHash']); } if (isset($keyData['scanSchedule']) && is_array($keyData['scanSchedule'])) { wfConfig::set_ser('noc1ScanSchedule', $keyData['scanSchedule']); if (wfScanner::shared()->schedulingMode() == wfScanner::SCAN_SCHEDULING_MODE_AUTOMATIC) { wfScanner::shared()->scheduleScans(); } } if (isset($keyData['showWfCentralUI'])) { wfConfig::set('showWfCentralUI', (int) $keyData['showWfCentralUI']); } if (isset($keyData['_keyNoLongerValid']) && $keyData['_keyNoLongerValid'] == 1) { if (wfConfig::get('keyDeletedNotice') !== $apiKey) { $keyDeletedNoticeSent = self::alert(__("The Wordfence Premium License in use on this site has been removed from your account.", 'wordfence'), __("The license you were using has been removed from your account. Please reach out to billing@wordfence.com or create a Premium support case at https://support.wordfence.com/support/tickets for more information. Our staff is happy to help.", 'wordfence'), false); if ($keyDeletedNoticeSent) { wfConfig::set('keyDeletedNotice', $apiKey); } } } wfConfig::set('keyType', $keyType); } catch(Exception $e){ wordfence::status(4, 'error', sprintf(/* translators: Wordfence license key. */ __("Could not verify Wordfence License: %s", 'wordfence'), $e->getMessage())); } } public static function dailyCron() { $lastDailyCron = (int) wfConfig::get('lastDailyCron', 0); if (($lastDailyCron + 43200) > time()) { //Run no more frequently than every 12 hours return; } wfConfig::set('lastDailyCron', time()); global $wpdb; $version = $wpdb->get_var("SELECT VERSION()"); wfConfig::set('dbVersion', $version); self::pingApiKey(); $allowMySQLi = wfConfig::testDB(); wfConfig::set('allowMySQLi', $allowMySQLi); $wfdb = new wfDB(); $table_wfLocs = wfDB::networkTable('wfLocs'); $wfdb->queryWrite("delete from {$table_wfLocs} where ctime < unix_timestamp() - %d", WORDFENCE_MAX_IPLOC_AGE); wfBlock::vacuum(); $table_wfCrawlers = wfDB::networkTable('wfCrawlers'); $wfdb->queryWrite("delete from {$table_wfCrawlers} where lastUpdate < unix_timestamp() - (86400 * 7)"); self::trimWfHits(true); $maxRows = absint(wfConfig::get('liveTraf_maxRows', 2000));; //affects stuff further down too $table_wfLogins = wfDB::networkTable('wfLogins'); $count2 = $wfdb->querySingle("select count(*) as cnt from {$table_wfLogins}"); if($count2 > 20000){ $wfdb->truncate($table_wfLogins); //in case of Dos } else if($count2 > $maxRows){ $wfdb->queryWrite("delete from {$table_wfLogins} order by ctime asc limit %d", ($count2 - $maxRows)); } wfCentral::trimSecurityEvents(); wfAuditLog::trimAuditEvents(); wfAuditLog::heartbeat(); $table_wfReverseCache = wfDB::networkTable('wfReverseCache'); $wfdb->queryWrite("delete from {$table_wfReverseCache} where unix_timestamp() - lastUpdate > 86400"); $table_wfStatus = wfDB::networkTable('wfStatus'); $count4 = $wfdb->querySingle("select count(*) as cnt from {$table_wfStatus}"); if($count4 > 100000){ $wfdb->truncate($table_wfStatus); } else if($count4 > 1000){ //max status events we keep. This determines how much gets emailed to us when users sends us a debug report. $wfdb->queryWrite("delete from {$table_wfStatus} where level != 10 order by ctime asc limit %d", ($count4 - 1000)); $count5 = $wfdb->querySingle("select count(*) as cnt from {$table_wfStatus} where level=10"); if($count5 > 100){ $wfdb->queryWrite("delete from {$table_wfStatus} where level = 10 order by ctime asc limit %d", ($count5 - 100) ); } } $report = new wfActivityReport(); $report->rotateIPLog(); self::_refreshUpdateNotification($report, true); wfUpdateCheck::syncAllVersionInfo(); self::purgeWafFailures(); wfConfig::remove('lastPermissionsTemplateCheck'); } public static function _scheduleRefreshUpdateNotification($upgrader = null, $options = null) { static $destructRegistered = false; $defer = false; if (is_array($options) && isset($options['type']) && $options['type'] == 'core') { $defer = true; set_site_transient('wordfence_updating_notifications', true, 600); } if ($defer) { wp_schedule_single_event(time(), 'wordfence_refreshUpdateNotification'); } else { if (!$destructRegistered) { register_shutdown_function(array(self::class, '_refreshUpdateNotification')); $destructRegistered = true; } } } public static function _refreshUpdateNotification($report = null, $useCachedValued = false) { if ($report === null) { $report = new wfActivityReport(); } $updatesNeeded = $report->getUpdatesNeeded($useCachedValued); if ($updatesNeeded) { $items = array(); $plural = false; if ($updatesNeeded['core']) { $items[] = sprintf(/* translators: WordPress version. */ __('WordPress (v%s)', 'wordfence'), esc_html($updatesNeeded['core'])); } if ($updatesNeeded['plugins']) { $entry = sprintf(/* translators: Number of plugins. */ _n('%d plugin', '%d plugins', count($updatesNeeded['plugins']), 'wordfence'), count($updatesNeeded['plugins'])); $items[] = $entry; } if ($updatesNeeded['themes']) { $entry = sprintf(/* translators: Number of themes. */ _n('%d theme', '%d themes', count($updatesNeeded['themes']), 'wordfence'), count($updatesNeeded['themes'])); $items[] = $entry; } $message = _n('An update is available for ', 'Updates are available for ', count($items), 'wordfence'); for ($i = 0; $i < count($items); $i++) { if ($i > 0 && count($items) > 2) { $message .= ', '; } else if ($i > 0) { $message .= ' '; } if ($i > 0 && $i == count($items) - 1) { $message .= __('and ', 'wordfence'); } $message .= $items[$i]; } new wfNotification(null, wfNotification::PRIORITY_HIGH_WARNING, '' . $message . '', 'wfplugin_updates'); } else { $n = wfNotification::getNotificationForCategory('wfplugin_updates'); if ($n !== null) { $n->markAsRead(); } } $i = new wfIssues(); $i->reconcileUpgradeIssues($report, true); wp_schedule_single_event(time(), 'wordfence_completeCoreUpdateNotification'); } public static function _completeCoreUpdateNotification() { //This approach is here because WP Core updates run in a different sequence than plugin/theme updates, so we have to defer the running of the notification update sequence by an extra page load delete_site_transient('wordfence_updating_notifications'); wfVersionCheckController::shared()->checkVersionsAndWarn(); } private static function scheduleCrons($delay = 0) { wp_clear_scheduled_hook('wordfence_daily_cron'); wp_clear_scheduled_hook('wordfence_hourly_cron'); if (is_main_site()) { wfConfig::remove('lastDailyCron'); wp_schedule_event(time() + $delay, 'daily', 'wordfence_daily_cron'); //'daily' wp_schedule_event(time() + $delay, 'hourly', 'wordfence_hourly_cron'); } } public static function runInstall(){ if(self::$runInstallCalled){ return; } self::$runInstallCalled = true; if (wfUtils::funcEnabled('ignore_user_abort')) { ignore_user_abort(true); } if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); } $previous_version = ((is_multisite() && function_exists('get_network_option')) ? get_network_option(null, 'wordfence_version', '0.0.0') : get_option('wordfence_version', '0.0.0')); if (is_multisite() && function_exists('update_network_option')) { update_network_option(null, 'wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install. } else { update_option('wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install. } wordfence::status(4, 'info', sprintf(/* translators: Wordfence version. */ __('`runInstall` called with previous version = %s', 'wordfence'), $previous_version)); //EVERYTHING HERE MUST BE IDEMPOTENT //Remove old legacy cron job if exists wp_clear_scheduled_hook('wordfence_scheduled_scan'); wfSchema::updateTableCase(); $schema = new wfSchema(); $schema->createAll(); //if not exists wfConfig::updateTableExists(true); /** @var wpdb $wpdb */ global $wpdb; //6.1.15 $configTable = wfDB::networkTable('wfConfig'); $hasAutoload = $wpdb->get_col($wpdb->prepare(<<query("ALTER TABLE {$configTable} ADD COLUMN autoload ENUM('no', 'yes') NOT NULL DEFAULT 'yes'"); $wpdb->query("UPDATE {$configTable} SET autoload = 'no' WHERE name = 'wfsd_engine' OR name LIKE 'wordfence_chunked_%'"); } $wpdb->query("DELETE FROM $configTable WHERE `name` = 'emailedIssuesList' AND LENGTH(`val`) > 2 * 1024 * 1024"); wfConfig::setDefaults(); //If not set $restOfSite = wfConfig::get('cbl_restOfSiteBlocked', 'notset'); if($restOfSite == 'notset'){ wfConfig::set('cbl_restOfSiteBlocked', '1'); } if(wfConfig::get('autoUpdate') == '1'){ wfConfig::enableAutoUpdate(); //Sets up the cron } $freshAPIKey = !wfConfig::get('apiKey'); if ($freshAPIKey) { wfConfig::set('touppPromptNeeded', true); } self::scheduleCrons(15); $db = new wfDB(); // IPv6 schema changes for 6.0.1 $tables_with_ips = array( 'wfCrawlers', 'wfBadLeechers', 'wfBlockedIPLog', 'wfBlocks', //Removed in 7.0.1 but left in in case migrating from really old 'wfHits', 'wfLocs', 'wfLogins', 'wfReverseCache', ); foreach ($tables_with_ips as $ip_table) { $ptable = wfDB::networkTable($ip_table); $tableExists = $wpdb->get_col($wpdb->prepare(<<get_row("SHOW FIELDS FROM {$ptable} where field = 'IP'"); if (!$result || strtolower($result->Type) == 'binary(16)') { continue; } $db->queryWriteIgnoreError("ALTER TABLE {$ptable} MODIFY IP BINARY(16)"); // Just to be sure we don't corrupt the data if the alter fails. $result = $wpdb->get_row("SHOW FIELDS FROM {$ptable} where field = 'IP'"); if (!$result || strtolower($result->Type) != 'binary(16)') { continue; } $db->queryWriteIgnoreError("UPDATE {$ptable} SET IP = CONCAT(LPAD(CHAR(0xff, 0xff), 12, CHAR(0)), LPAD( CHAR( CAST(IP as UNSIGNED) >> 24 & 0xFF, CAST(IP as UNSIGNED) >> 16 & 0xFF, CAST(IP as UNSIGNED) >> 8 & 0xFF, CAST(IP as UNSIGNED) & 0xFF ), 4, CHAR(0) ))"); } //Country reassignment moved to the GeoIP file sync segment if (wfConfig::get('other_hideWPVersion')) { wfUtils::hideReadme(); } $colsFor610 = array( 'attackLogTime' => '`attackLogTime` double(17,6) unsigned NOT NULL AFTER `id`', 'statusCode' => '`statusCode` int(11) NOT NULL DEFAULT 0 AFTER `jsRun`', 'action' => "`action` varchar(64) NOT NULL DEFAULT '' AFTER `UA`", 'actionDescription' => '`actionDescription` text AFTER `action`', 'actionData' => '`actionData` text AFTER `actionDescription`', ); $hitTable = wfDB::networkTable('wfHits'); foreach ($colsFor610 as $col => $colDefintion) { $count = $wpdb->get_col($wpdb->prepare(<<query("ALTER TABLE $hitTable ADD COLUMN $colDefintion"); } } $has404 = $wpdb->get_col($wpdb->prepare(<<query(<<query("ALTER TABLE $hitTable DROP COLUMN `is404`"); } $loginsTable = wfDB::networkTable('wfLogins'); $hasHitID = $wpdb->get_col($wpdb->prepare(<<query("ALTER TABLE $loginsTable ADD COLUMN hitID int(11) DEFAULT NULL AFTER `id`, ADD INDEX(hitID)"); } if (!WFWAF_SUBDIRECTORY_INSTALL) { wfWAFConfig::set('wafDisabled', false); } // Call this before creating the index in cases where the wp-cron isn't running. self::trimWfHits(true); $hitsTable = wfDB::networkTable('wfHits'); $hasAttackLogTimeIndex = $wpdb->get_var($wpdb->prepare(<<query("ALTER TABLE $hitsTable ADD INDEX `attackLogTime` (`attackLogTime`)"); } //6.1.16 $allowed404s = wfConfig::get('allowed404s', ''); if (!wfConfig::get('allowed404s6116Migration', false)) { if (!preg_match('/(?:^|\b)browserconfig\.xml(?:\b|$)/i', $allowed404s)) { if (strlen($allowed404s) > 0) { $allowed404s .= "\n"; } $allowed404s .= "/browserconfig.xml"; wfConfig::set('allowed404s', $allowed404s); } wfConfig::set('allowed404s6116Migration', 1); } if (wfConfig::get('email_summary_interval') == 'biweekly') { wfConfig::set('email_summary_interval', 'weekly'); } //6.2.0 wfConfig::migrateCodeExecutionForUploadsPHP7(); //6.2.3 if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings(); //changed slightly for 7.0.1 } //6.2.8 wfCache::removeCaching(); //6.2.10 $snipCacheTable = wfDB::networkTable('wfSNIPCache'); $hasType = $wpdb->get_col($wpdb->prepare(<<query("ALTER TABLE `{$snipCacheTable}` ADD `type` INT UNSIGNED NOT NULL DEFAULT '0'"); $wpdb->query("ALTER TABLE `{$snipCacheTable}` ADD INDEX (`type`)"); } //6.3.5 $fileModsTable = wfDB::networkTable('wfFileMods'); $hasStoppedOn = $wpdb->get_col($wpdb->prepare(<<query("ALTER TABLE {$fileModsTable} ADD COLUMN stoppedOnSignature VARCHAR(255) NOT NULL DEFAULT ''"); $wpdb->query("ALTER TABLE {$fileModsTable} ADD COLUMN stoppedOnPosition INT UNSIGNED NOT NULL DEFAULT '0'"); } $blockedIPLogTable = wfDB::networkTable('wfBlockedIPLog'); $hasType = $wpdb->get_col($wpdb->prepare(<<query("ALTER TABLE {$blockedIPLogTable} ADD blockType VARCHAR(50) NOT NULL DEFAULT 'generic'"); $wpdb->query("ALTER TABLE {$blockedIPLogTable} DROP PRIMARY KEY"); $wpdb->query("ALTER TABLE {$blockedIPLogTable} ADD PRIMARY KEY (IP, unixday, blockType)"); } //6.3.6 if (!wfConfig::get('migration636_email_summary_excluded_directories')) { $excluded_directories = explode(',', (string) wfConfig::get('email_summary_excluded_directories')); $key = array_search('wp-content/plugins/wordfence/tmp', $excluded_directories); if ($key !== false) { unset($excluded_directories[$key]); } $key = array_search('wp-content/wflogs', $excluded_directories); if ($key === false) { $excluded_directories[] = 'wp-content/wflogs'; } wfConfig::set('email_summary_excluded_directories', implode(',', $excluded_directories)); wfConfig::set('migration636_email_summary_excluded_directories', 1, wfConfig::DONT_AUTOLOAD); } $fileModsTable = wfDB::networkTable('wfFileMods'); $hasSHAC = $wpdb->get_col($wpdb->prepare(<<query("ALTER TABLE {$fileModsTable} ADD COLUMN `SHAC` BINARY(32) NOT NULL DEFAULT '' AFTER `newMD5`"); $wpdb->query("ALTER TABLE {$fileModsTable} ADD COLUMN `isSafeFile` VARCHAR(1) NOT NULL DEFAULT '?' AFTER `stoppedOnPosition`"); } //6.3.7 $hooverTable = wfDB::networkTable('wfHoover'); $hostKeySize = $wpdb->get_var($wpdb->prepare(<<query("ALTER TABLE {$hooverTable} CHANGE `hostKey` `hostKey` VARBINARY(124) NULL DEFAULT NULL"); } //6.3.15 $scanFileContents = wfConfig::get('scansEnabled_fileContents', false); if (!wfConfig::get('fileContentsGSB6315Migration', false)) { if (!$scanFileContents) { wfConfig::set('scansEnabled_fileContentsGSB', false); } wfConfig::set('fileContentsGSB6315Migration', 1); } //6.3.20 $lastBlockAggregation = wfConfig::get('lastBlockAggregation', 0); if ($lastBlockAggregation == 0) { wfConfig::set('lastBlockAggregation', time()); } //7.0.1 //---- Config Migration if (!wfConfig::get('config701Migration', false)) { //loginSec_strongPasswds gains a toggle if (wfConfig::get('loginSec_strongPasswds') == '') { wfConfig::set('loginSec_strongPasswds', 'pubs'); wfConfig::set('loginSec_strongPasswds_enabled', false); } $limitedOptions = wfScanner::limitedScanTypeOptions(); $standardOptions = wfScanner::standardScanTypeOptions(); $highSensitivityOptions = wfScanner::highSensitivityScanTypeOptions(); $settings = wfScanner::customScanTypeOptions(); if ($settings == $limitedOptions) { wfConfig::set('scanType', wfScanner::SCAN_TYPE_LIMITED); } else if ($settings == $standardOptions) { wfConfig::set('scanType', wfScanner::SCAN_TYPE_STANDARD); } else if ($settings == $highSensitivityOptions) { wfConfig::set('scanType', wfScanner::SCAN_TYPE_HIGH_SENSITIVITY); } else { wfConfig::set('scanType', wfScanner::SCAN_TYPE_CUSTOM); } if (wfConfig::get('isPaid')) { wfConfig::set('keyType', wfLicense::KEY_TYPE_PAID_CURRENT); } wfConfig::remove('premiumAutoRenew'); wfConfig::remove('premiumNextRenew'); wfConfig::remove('premiumPaymentExpiring'); wfConfig::remove('premiumPaymentExpired'); wfConfig::remove('premiumPaymentMissing'); wfConfig::remove('premiumPaymentHold'); wfConfig::set('config701Migration', 1); } //---- wfBlocks migration $oldBlocksTable = wfDB::networkTable('wfBlocks'); $blocksTable = wfBlock::blocksTable(); $oldBlocksExist = $wpdb->get_col($wpdb->prepare(<<prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`) SELECT CASE WHEN wfsn = 1 AND permanent = 0 THEN %d WHEN wfsn = 0 AND permanent = 0 THEN %d WHEN wfsn = 0 AND permanent = 1 THEN %d END AS `type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, CASE WHEN wfsn = 1 AND permanent = 0 THEN (`blockedTime` + 600) WHEN wfsn = 0 AND permanent = 0 THEN (`blockedTime` + %d) WHEN wfsn = 0 AND permanent = 1 THEN 0 END AS `expiration` FROM `{$oldBlocksTable}`", wfBlock::TYPE_WFSN_TEMPORARY, wfBlock::TYPE_RATE_BLOCK, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT, wfConfig::get('blockedTime')); $wpdb->query($query); //wfBlocksAdv migration $advancedBlocksTable = wfDB::networkTable('wfBlocksAdv'); $advancedBlocks = $wpdb->get_results("SELECT * FROM {$advancedBlocksTable}", ARRAY_A); foreach ($advancedBlocks as $b) { $blockType = $b['blockType']; //unused $blockString = $b['blockString']; $ctime = (int) $b['ctime']; $reason = $b['reason']; $totalBlocked = (int) $b['totalBlocked']; $lastBlocked = (int) $b['lastBlocked']; list($ipRange, $uaRange, $referrer, $hostname) = explode('|', $blockString); wfBlock::createPattern($reason, $ipRange, $hostname, $uaRange, $referrer, wfBlock::DURATION_FOREVER, $ctime, $lastBlocked, $totalBlocked); } //throttle migration $throttleTable = wfDB::networkTable('wfThrottleLog'); $throttles = $wpdb->get_results("SELECT * FROM {$throttleTable}", ARRAY_A); foreach ($throttles as $t) { $ip = wfUtils::inet_ntop($t['IP']); $startTime = (int) $t['startTime']; $endTime = (int) $t['endTime']; $timesThrottled = (int) $t['timesThrottled']; $reason = $t['lastReason']; wfBlock::createRateThrottle($reason, $ip, wfBlock::rateLimitThrottleDuration(), $startTime, $endTime, $timesThrottled); } //lockout migration $lockoutTable = wfDB::networkTable('wfLockedOut'); $lockouts = $wpdb->get_results("SELECT * FROM {$lockoutTable}", ARRAY_A); foreach ($lockouts as $l) { $ip = wfUtils::inet_ntop($l['IP']); $blockedTime = (int) $l['blockedTime']; $reason = $l['reason']; $lastAttempt = (int) $l['lastAttempt']; $blockedHits = (int) $l['blockedHits']; wfBlock::createLockout($reason, $ip, wfBlock::lockoutDuration(), $blockedTime, $lastAttempt, $blockedHits); } //country blocking migration $countries = wfConfig::get('cbl_countries', false); if ($countries) { $countries = explode(',', $countries); wfBlock::createCountry(__('Automatically generated from previous country blocking settings', 'wordfence'), wfConfig::get('cbl_loginFormBlocked', false), wfConfig::get('cbl_restOfSiteBlocked', false), $countries); } wfConfig::set('blocks701Migration', 1); } //---- wfIssues/wfPendingIssues Schema Change $issuesTable = wfDB::networkTable('wfIssues'); $pendingIssuesTable = wfDB::networkTable('wfPendingIssues'); $hasLastUpdated = $wpdb->get_col($wpdb->prepare(<<query("ALTER TABLE `{$issuesTable}` ADD `lastUpdated` INT UNSIGNED NOT NULL AFTER `time`"); $wpdb->query("ALTER TABLE `{$issuesTable}` ADD INDEX (`lastUpdated`)"); $wpdb->query("ALTER TABLE `{$issuesTable}` ADD INDEX (`status`)"); $wpdb->query("ALTER TABLE `{$issuesTable}` ADD INDEX (`ignoreP`)"); $wpdb->query("ALTER TABLE `{$issuesTable}` ADD INDEX (`ignoreC`)"); $wpdb->query("UPDATE `{$issuesTable}` SET `lastUpdated` = `time` WHERE `lastUpdated` = 0"); $wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD `lastUpdated` INT UNSIGNED NOT NULL AFTER `time`"); $wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD INDEX (`lastUpdated`)"); $wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD INDEX (`status`)"); $wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD INDEX (`ignoreP`)"); $wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD INDEX (`ignoreC`)"); } //---- Scheduled scan start hour and manual type if (wfConfig::get('schedStartHour') < 0) { wfConfig::set('schedStartHour', wfWAFUtils::random_int(0, 23)); if (wfConfig::get('schedMode') == 'manual') { $sched = wfConfig::get_ser('scanSched', array()); if (is_array($sched) && is_array($sched[0])) { //Try to determine the closest matching value for manualScanType $hours = array_fill(0, 24, 0); $distinctHours = array(); $days = array_fill(0, 7, 0); $distinctDays = array(); foreach ($sched as $dayIndex => $day) { foreach ($day as $h => $enabled) { if ($enabled) { if (in_array($h, $distinctHours)) { $distinctHours[] = $h; } $hours[$h]++; if (in_array($dayIndex, $distinctDays)) { $distinctDays[] = $dayIndex; } $days[$dayIndex]++; } } } sort($distinctHours, SORT_NUMERIC); sort($distinctDays, SORT_NUMERIC); if (count($distinctDays) == 7) { if (count($distinctHours) == 1) { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_ONCE_DAILY); wfConfig::set('schedStartHour', $distinctHours[0]); } else if (count($distinctHours) == 2) { $matchesTwiceDaily = false; if ($distinctHours[0] + 12 == $distinctHours[1]) { $matchesTwiceDaily = true; foreach ($sched as $dayIndex => $day) { if (!$day[$distinctHours[0]] || !$day[$distinctHours[1]]) { $matchesTwiceDaily = false; } } } if ($matchesTwiceDaily) { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_TWICE_DAILY); wfConfig::set('schedStartHour', $distinctHours[0]); } else { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM); } } else { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM); } } else if (count($distinctDays) == 5 && count($distinctHours) == 1) { if ($days[2] == 0 && $days[4] == 0 && $hours[$distinctHours[0]] == 5) { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_ODD_DAYS_WEEKENDS); wfConfig::set('schedStartHour', $distinctHours[0]); } else if ($days[0] == 0 && $days[6] == 0 && $hours[$distinctHours[0]] == 5) { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_WEEKDAYS); wfConfig::set('schedStartHour', $distinctHours[0]); } else { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM); } } else if (count($distinctDays) == 2 && count($distinctHours) == 1) { if ($distinctDays[0] == 0 && $distinctDays[1] == 6 && $hours[$distinctHours[0]] == 2) { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_WEEKENDS); wfConfig::set('schedStartHour', $distinctHours[0]); } else { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM); } } else { wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM); } } //manualScanType } } //---- Onboarding if (!$freshAPIKey) { wfOnboardingController::migrateOnboarding(); } //7.0.2 if (!wfConfig::get('blocks702Migration')) { $blocksTable = wfBlock::blocksTable(); $query = "UPDATE `{$blocksTable}` SET `type` = %d WHERE `type` = %d AND `parameters` IS NOT NULL AND `parameters` LIKE '%\"ipRange\"%'"; $wpdb->query($wpdb->prepare($query, wfBlock::TYPE_PATTERN, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT)); $countryBlock = wfBlock::countryBlocks(); if (!count($countryBlock)) { $query = "UPDATE `{$blocksTable}` SET `type` = %d WHERE `type` = %d AND `parameters` IS NOT NULL AND `parameters` LIKE '%\"blockLogin\"%' LIMIT 1"; $wpdb->query($wpdb->prepare($query, wfBlock::TYPE_COUNTRY, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT)); } $query = "DELETE FROM `{$blocksTable}` WHERE `type` = %d AND `parameters` IS NOT NULL AND `parameters` LIKE '%\"blockLogin\"%'"; $wpdb->query($wpdb->prepare($query, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT)); wfConfig::set('blocks702Migration', 1); } //7.0.3 /*if (!wfConfig::get('generateAllOptionsNotification')) { new wfNotification(null, wfNotification::PRIORITY_HIGH_WARNING, '

Developers: If you prefer to edit all Wordfence options on one page, you can enable the "All Options" page here:

Enable "All Options" Page

', 'wfplugin_devalloptions'); wfConfig::set('generateAllOptionsNotification', 1); }*/ //7.1.9 if (wfConfig::get('loginSec_maxFailures') == 1) { wfConfig::set('loginSec_maxFailures', 2); } $blocksTable = wfBlock::blocksTable(); $patternBlocks = wfBlock::patternBlocks(); foreach ($patternBlocks as $b) { if (!empty($b->ipRange) && preg_match('/^\d+\-\d+$/', $b->ipRange)) { //Old-style range block using long2ip $ipRange = new wfUserIPRange($b->ipRange); $ipRange = $ipRange->getIPString(); $parameters = $b->parameters; $parameters['ipRange'] = $ipRange; $wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `parameters` = %s WHERE `id` = %d", json_encode($parameters), $b->id)); } } wfConfig::set('needsGeoIPSync', true, wfConfig::DONT_AUTOLOAD); // Set the default scan options based on scan type. if (!wfConfig::get('config720Migration', false)) { // Replace critical/warning checkboxes with setting based on numeric severity value. if (wfConfig::hasCachedOption('alertOn_critical') && wfConfig::hasCachedOption('alertOn_warnings')) { $alertOnCritical = wfConfig::get('alertOn_critical'); $alertOnWarnings = wfConfig::get('alertOn_warnings'); wfConfig::set('alertOn_scanIssues', $alertOnCritical || $alertOnWarnings); if ($alertOnCritical && ! $alertOnWarnings) { wfConfig::set('alertOn_severityLevel', wfIssues::SEVERITY_HIGH); } else { wfConfig::set('alertOn_severityLevel', wfIssues::SEVERITY_LOW); } } // Update severity for existing issues where they are still using the old severity values. foreach (wfIssues::$issueSeverities as $issueType => $severity) { $wpdb->query($wpdb->prepare("UPDATE $issuesTable SET severity = %d WHERE `type` = %s AND severity in (0,1,2) ", $severity, $issueType)); } $syncedOptions = array(); switch (wfConfig::get('scanType')) { case wfScanner::SCAN_TYPE_LIMITED: $syncedOptions = wfScanner::limitedScanTypeOptions(); break; case wfScanner::SCAN_TYPE_STANDARD: $syncedOptions = wfScanner::standardScanTypeOptions(); break; case wfScanner::SCAN_TYPE_HIGH_SENSITIVITY: $syncedOptions = wfScanner::highSensitivityScanTypeOptions(); break; } if ($syncedOptions) { foreach ($syncedOptions as $key => $value) { if (is_bool($value)) { wfConfig::set($key, $value ? 1 : 0); } } } wfConfig::set('config720Migration', true); } //7.2.3 if (wfConfig::get('waf_status') === false) { $firewall = new wfFirewall(); $firewall->syncStatus(true); } //7.3.1 //---- drop long deprecated tables $tables = array('wfBadLeechers', 'wfBlockedCommentLog', 'wfBlocks', 'wfBlocksAdv', 'wfLeechers', 'wfLockedOut', 'wfNet404s', 'wfScanners', 'wfThrottleLog', 'wfVulnScanners'); foreach ($tables as $t) { $schema->drop($t); } //7.5.10 $knownFilesTable = wfDB::networkTable('wfKnownFileList'); $wordpressPathColumn = $wpdb->get_row($wpdb->prepare("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = 'wordpress_path'", $knownFilesTable)); if ($wordpressPathColumn === null) { $wpdb->query("DELETE FROM `{$knownFilesTable}`"); $wpdb->query("ALTER TABLE `{$knownFilesTable}` ADD COLUMN wordpress_path TEXT NOT NULL"); } $realPathColumn = $wpdb->get_row($wpdb->prepare("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = 'real_path'", $fileModsTable)); if ($realPathColumn === null) { $wpdb->query("DELETE FROM `{$fileModsTable}`"); $wpdb->query("ALTER TABLE `{$fileModsTable}` ADD COLUMN real_path TEXT NOT NULL AFTER filename"); } //wfFileMods updates $wpdb->query("ALTER TABLE {$fileModsTable} ALTER COLUMN oldMD5 SET DEFAULT ''"); //---- enable legacy 2fa if applicable if (wfConfig::get('isPaid') && (wfCredentialsController::hasOld2FARecords() || version_compare(phpversion(), '5.3', '<'))) { wfConfig::set(wfCredentialsController::ALLOW_LEGACY_2FA_OPTION, true); } //Record the installation timestamp if activating the plugin for the first time if (get_option('wordfenceActivated') != 1 && wfConfig::get('satisfactionPromptInstallDate') == 0 && empty(wfConfig::get('apiKey'))) { wfConfig::set('satisfactionPromptInstallDate', time()); } //Check the How does Wordfence get IPs setting wfUtils::requestDetectProxyCallback(); //Install new schedule. If schedule config is blank it will install the default 'auto' schedule. wfScanner::shared()->scheduleScans(); //Check our minimum versions and generate the necessary warnings if (!wp_next_scheduled('wordfence_version_check')) { wp_schedule_single_event(time(), 'wordfence_version_check'); } //Must be the final line } private static function doEarlyAccessLogging(){ $wfLog = self::getLog(); if($wfLog->logHitOK()){ $request = $wfLog->getCurrentRequest(); if(is_404()){ if ($request) { $request->statusCode = 404; } $wfLog->logLeechAndBlock('404'); } else { $wfLog->logLeechAndBlock('hit'); } } } public static function initProtection(){ //Basic protection during WAF learning period // Infinite WP Client - Authentication Bypass < 1.9.4.5 // https://wpvulndb.com/vulnerabilities/10011 $iwpRule = new wfWAFRule(wfWAF::getInstance(), 0x80000000, null, 'auth-bypass', 100, 'Infinite WP Client - Authentication Bypass < 1.9.4.5', 0, 'block', null); wfWAF::getInstance()->setRules(wfWAF::getInstance()->getRules() + array(0x80000000 => $iwpRule)); if (strrpos(wfWAF::getInstance()->getRequest()->getRawBody(), '_IWP_JSON_PREFIX_') !== false) { $iwpRequestDataArray = explode('_IWP_JSON_PREFIX_', wfWAF::getInstance()->getRequest()->getRawBody()); $iwpRequest = json_decode(trim(base64_decode($iwpRequestDataArray[1])), true); if (is_array($iwpRequest)) { if (array_key_exists('iwp_action', $iwpRequest) && ($iwpRequest['iwp_action'] === 'add_site' || $iwpRequest['iwp_action'] === 'readd_site') ) { require_once ABSPATH . '/wp-admin/includes/plugin.php'; if (is_plugin_active('iwp-client/init.php')) { $iwpPluginData = get_plugin_data(WP_PLUGIN_DIR . '/iwp-client/init.php'); if (version_compare('1.9.4.5', $iwpPluginData['Version'], '>')) { remove_action('setup_theme', 'iwp_mmb_set_request'); } } if ((is_multisite() ? get_site_option('iwp_client_action_message_id') : get_option('iwp_client_action_message_id')) && (is_multisite() ? get_site_option('iwp_client_public_key') : get_option('iwp_client_public_key')) ) { wfWAF::getInstance()->getStorageEngine()->logAttack(array($iwpRule), 'request.rawBody', wfWAF::getInstance()->getRequest()->getRawBody(), wfWAF::getInstance()->getRequest(), wfWAF::getInstance()->getRequest()->getMetadata() ); } } } } } public static function install_actions(){ register_activation_hook(WORDFENCE_FCPATH, 'wordfence::installPlugin'); register_deactivation_hook(WORDFENCE_FCPATH, 'wordfence::uninstallPlugin'); $versionInOptions = ((is_multisite() && function_exists('get_network_option')) ? get_network_option(null, 'wordfence_version', false) : get_option('wordfence_version', false)); if( (! $versionInOptions) || version_compare(WORDFENCE_VERSION, $versionInOptions, '>')){ //Either there is no version in options or the version in options is greater and we need to run the upgrade self::runInstall(); } self::getLog()->initLogRequest(); //Fix wp_mail bug when $_SERVER['SERVER_NAME'] is undefined add_filter('wp_mail_from', 'wordfence::fixWPMailFromAddress'); //These access wfConfig::get('apiKey') and will fail if runInstall hasn't executed. if(defined('MULTISITE') && MULTISITE === true){ global $blog_id; if($blog_id == 1 && get_option('wordfenceActivated') != 1){ return; } //Because the plugin is active once installed, even before it's network activated, for site 1 (WordPress team, why?!) } //User may be logged in or not, so register both handlers add_action('wp_ajax_nopriv_wordfence_lh', 'wordfence::ajax_lh_callback'); add_action('wp_ajax_nopriv_wordfence_doScan', 'wordfence::ajax_doScan_callback'); add_action('wp_ajax_nopriv_wordfence_testAjax', 'wordfence::ajax_testAjax_callback'); if(wfUtils::hasLoginCookie()){ //may be logged in. Fast way to check. These aren't secure functions, this is just a perf optimization, along with every other use of hasLoginCookie() add_action('wp_ajax_wordfence_lh', 'wordfence::ajax_lh_callback'); add_action('wp_ajax_wordfence_doScan', 'wordfence::ajax_doScan_callback'); add_action('wp_ajax_wordfence_testAjax', 'wordfence::ajax_testAjax_callback'); if (is_multisite()) { add_action('wp_network_dashboard_setup', 'wordfence::addDashboardWidget'); } else { add_action('wp_dashboard_setup', 'wordfence::addDashboardWidget'); } } add_action('wp_ajax_wordfence_wafStatus', 'wordfence::ajax_wafStatus_callback'); add_action('wp_ajax_nopriv_wordfence_wafStatus', 'wordfence::ajax_wafStatus_callback'); add_action('wp_ajax_nopriv_wordfence_remoteVerifySwitchTo2FANew', 'wordfence::ajax_remoteVerifySwitchTo2FANew_callback'); add_action('wordfence_start_scheduled_scan', 'wordfence::wordfenceStartScheduledScan'); add_action('wordfence_daily_cron', 'wordfence::dailyCron'); add_action('wordfence_daily_autoUpdate', 'wfConfig::autoUpdate'); add_action('wordfence_hourly_cron', 'wordfence::hourlyCron'); add_action('wordfence_version_check', array(wfVersionCheckController::shared(), 'checkVersionsAndWarn')); add_action('plugins_loaded', 'wordfence::veryFirstAction'); add_action('init', 'wordfence::initAction'); //add_action('admin_bar_menu', 'wordfence::admin_bar_menu', 99); add_action('template_redirect', 'wordfence::templateRedir', 1001); add_action('shutdown', 'wordfence::shutdownAction'); if (!wfConfig::get('ajaxWatcherDisabled_front')) { add_action('wp_enqueue_scripts', 'wordfence::enqueueAJAXWatcher'); } if (!wfConfig::get('ajaxWatcherDisabled_admin')) { add_action('admin_enqueue_scripts', 'wordfence::enqueueAJAXWatcher'); } //add_action('wp_enqueue_scripts', 'wordfence::enqueueDashboard'); add_action('admin_enqueue_scripts', 'wordfence::enqueueDashboard'); add_action('wp_authenticate','wordfence::authAction', 1, 2); add_action('wp_authenticate_user', 'wordfence::authUserAction', 1, 2); //A secondary lockout check for plugins that override the login flow and don't call the complete set of hooks add_filter('authenticate', 'wordfence::authenticateFilter', 99, 3); $lockout = wfBlock::lockoutForIP(wfUtils::getIP()); if ($lockout !== false) { add_filter('xmlrpc_enabled', '__return_false'); } add_action('login_init','wordfence::loginInitAction'); add_action('wp_login','wordfence::loginAction'); add_action('wp_logout','wordfence::logoutAction'); add_action('lostpassword_post', 'wordfence::lostPasswordPost', 1, 2); $allowSeparatePrompt = ini_get('output_buffering') > 0; if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) { add_action('login_form', 'wordfence::showTwoFactorField'); } if(wfUtils::hasLoginCookie()){ add_action('user_profile_update_errors', 'wordfence::validateProfileUpdate', 0, 3 ); add_action('profile_update', 'wordfence::profileUpdateAction', 99, 2); } add_action('validate_password_reset', 'wordfence::validatePassword', 10, 2); // Add actions for the email summary add_action('wordfence_email_activity_report', array('wfActivityReport', 'executeCronJob')); //For debugging //add_filter( 'cron_schedules', 'wordfence::cronAddSchedules' ); add_filter('wp_redirect', 'wordfence::wpRedirectFilter', 99, 2); add_filter('wp_redirect_status', 'wordfence::wpRedirectStatusFilter', 99, 2); //html|xhtml|atom|rss2|rdf|comment|export if(wfConfig::get('other_hideWPVersion')){ add_filter('style_loader_src', 'wordfence::replaceVersion'); add_filter('script_loader_src', 'wordfence::replaceVersion'); add_action('upgrader_process_complete', 'wordfence::hideReadme'); } add_filter('get_the_generator_html', 'wordfence::genFilter', 99, 2); add_filter('get_the_generator_xhtml', 'wordfence::genFilter', 99, 2); add_filter('get_the_generator_atom', 'wordfence::genFilter', 99, 2); add_filter('get_the_generator_rss2', 'wordfence::genFilter', 99, 2); add_filter('get_the_generator_rdf', 'wordfence::genFilter', 99, 2); add_filter('get_the_generator_comment', 'wordfence::genFilter', 99, 2); add_filter('get_the_generator_export', 'wordfence::genFilter', 99, 2); add_filter('registration_errors', 'wordfence::registrationFilter', 99, 3); add_filter('woocommerce_new_customer_data', 'wordfence::wooRegistrationFilter', 99, 1); if (wfConfig::get('loginSec_disableAuthorScan')) { add_filter('oembed_response_data', 'wordfence::oembedAuthorFilter', 99, 4); add_filter('rest_request_before_callbacks', 'wordfence::jsonAPIAuthorFilter', 99, 3); add_filter('rest_post_dispatch', 'wordfence::jsonAPIAdjustHeaders', 99, 3); add_filter('wp_sitemaps_users_pre_url_list', '__return_false', 99, 0); add_filter('wp_sitemaps_add_provider', 'wordfence::wpSitemapUserProviderFilter', 99, 2); } if (wfConfig::get('loginSec_disableApplicationPasswords')) { add_filter('wp_is_application_passwords_available', '__return_false'); add_action('edit_user_profile', 'wordfence::showDisabledApplicationPasswordsMessage', -1); add_action('show_user_profile', 'wordfence::showDisabledApplicationPasswordsMessage', -1); // Override the wp_die handler to let the user know app passwords were disabled by the Wordfence option. if (!empty($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] === ABSPATH . 'wp-admin/authorize-application.php') { add_filter('wp_die_handler', function ($handler = null) { return function ($message, $title, $args) { if ($message === 'Application passwords are not available.') { $message = __('Application passwords have been disabled by Wordfence.', 'wordfence'); } _default_wp_die_handler($message, $title, $args); }; }, 10, 1); } } add_filter('rest_dispatch_request', 'wordfence::_filterCentralFromLiveTraffic', 99, 4); // Change GoDaddy's limit login mu-plugin since it can interfere with the two factor auth message. if (self::hasGDLimitLoginsMUPlugin()) { add_action('login_errors', array('wordfence', 'fixGDLimitLoginsErrors'), 11); } add_action('upgrader_process_complete', 'wfUpdateCheck::syncAllVersionInfo'); add_action('upgrader_process_complete', 'wordfence::_scheduleRefreshUpdateNotification', 99, 2); add_action('automatic_updates_complete', 'wordfence::_scheduleRefreshUpdateNotification', 99, 0); add_action('wordfence_refreshUpdateNotification', 'wordfence::_refreshUpdateNotification', 99, 0); add_action('wordfence_completeCoreUpdateNotification', 'wordfence::_completeCoreUpdateNotification', 99, 0); add_action('wfls_xml_rpc_blocked', 'wordfence::checkSecurityNetwork'); add_action('wfls_registration_blocked', 'wordfence::checkSecurityNetwork'); add_action('wfls_activation_page_footer', 'wordfence::_outputLoginSecurityTour'); add_action('wfls_settings_set', 'wordfence::queueCentralConfigurationSync', 10, 2); if(is_admin()){ add_action('admin_init', 'wordfence::admin_init'); add_action('admin_head', 'wordfence::_retargetWordfenceSubmenuCallout'); if(is_multisite()){ if(wfUtils::isAdminPageMU()){ add_action('network_admin_menu', 'wordfence::admin_menus', 10); add_action('network_admin_menu', 'wordfence::admin_menus_20', 20); add_action('network_admin_menu', 'wordfence::admin_menus_30', 30); add_action('network_admin_menu', 'wordfence::admin_menus_40', 40); add_action('network_admin_menu', 'wordfence::admin_menus_50', 50); add_action('network_admin_menu', 'wordfence::admin_menus_60', 60); add_action('network_admin_menu', 'wordfence::admin_menus_70', 70); add_action('network_admin_menu', 'wordfence::admin_menus_80', 80); add_action('network_admin_menu', 'wordfence::admin_menus_85', 85); add_action('network_admin_menu', 'wordfence::admin_menus_90', 90); } //else don't show menu } else { add_action('admin_menu', 'wordfence::admin_menus', 10); add_action('admin_menu', 'wordfence::admin_menus_20', 20); add_action('admin_menu', 'wordfence::admin_menus_30', 30); add_action('admin_menu', 'wordfence::admin_menus_40', 40); add_action('admin_menu', 'wordfence::admin_menus_50', 50); add_action('admin_menu', 'wordfence::admin_menus_60', 60); add_action('admin_menu', 'wordfence::admin_menus_70', 70); add_action('admin_menu', 'wordfence::admin_menus_80', 80); add_action('admin_menu', 'wordfence::admin_menus_85', 85); add_action('admin_menu', 'wordfence::admin_menus_90', 90); } add_filter('plugin_action_links_' . plugin_basename(realpath(dirname(__FILE__) . '/../wordfence.php')), 'wordfence::_pluginPageActionLinks'); add_filter('pre_current_active_plugins', 'wordfence::registerDeactivationPrompt'); } add_action('request', 'wordfence::preventAuthorNScans'); add_action('password_reset', 'wordfence::actionPasswordReset'); $adminUsers = new wfAdminUserMonitor(); if ($adminUsers->isEnabled()) { add_action('set_user_role', array($adminUsers, 'updateToUserRole'), 10, 3); add_action('grant_super_admin', array($adminUsers, 'grantSuperAdmin'), 10, 1); add_action('revoke_super_admin', array($adminUsers, 'revokeSuperAdmin'), 10, 1); } else if (wfConfig::get_ser('adminUserList', false)) { // reset this in the event it's disabled or the network is too large wfConfig::set_ser('adminUserList', false); } if (wfConfig::liveTrafficEnabled()) { add_action('wp_head', 'wordfence::wfLogHumanHeader'); add_action('login_head', 'wordfence::wfLogHumanHeader'); } add_action('wordfence_processAttackData', 'wordfence::processAttackData'); if (!empty($_GET['wordfence_syncAttackData']) && get_site_option('wordfence_syncingAttackData') <= time() - 60 && get_site_option('wordfence_lastSyncAttackData', 0) < time() - 8) { if (wfUtils::funcEnabled('ignore_user_abort')) { ignore_user_abort(true); } update_site_option('wordfence_syncingAttackData', time()); header('Content-Type: text/javascript'); define('WORDFENCE_SYNCING_ATTACK_DATA', true); add_action('init', 'wordfence::syncAttackData', 10, 0); add_filter('woocommerce_unforce_ssl_checkout', '__return_false'); } add_action('wordfence_batchReportBlockedAttempts', 'wordfence::wfsnBatchReportBlockedAttempts'); add_action('wordfence_batchReportFailedAttempts', 'wordfence::wfsnBatchReportFailedAttempts'); add_action('wordfence_batchSendSecurityEvents', 'wfCentral::sendPendingSecurityEvents'); add_action('wordfence_batchSendAuditEvents', 'wfAuditLog::sendPendingAuditEvents'); if (wfConfig::get('other_hideWPVersion')) { add_filter('update_feedback', 'wordfence::restoreReadmeForUpgrade'); } add_action('rest_api_init', 'wordfence::initRestAPI'); if (wfCentral::isConnected()) { add_action('wordfence_security_event', 'wfCentral::sendSecurityEvent', 10, 3); } else { add_action('wordfence_security_event', 'wfCentral::sendAlertCallback', 10, 3); } if (!wfConfig::get('wordfenceI18n', true)) { add_filter('gettext', function ($translation, $text, $domain) { if ($domain === 'wordfence') { return $text; } return $translation; }, 10, 3); } wfScanMonitor::registerActions(); wfUpdateCheck::installPluginAPIFixer(); wfAuditLog::shared()->registerHooks(); } public static function registerDeactivationPrompt() { $deleteMain = (bool) wfConfig::get('deleteTablesOnDeact'); $deleteLoginSecurity = (bool) \WordfenceLS\Controller_Settings::shared()->get('delete-deactivation'); echo wfView::create( 'offboarding/deactivation-prompt', array( 'deactivationOption' => wfDeactivationOption::forState($deleteMain, $deleteLoginSecurity), 'wafOptimized' => defined('WFWAF_AUTO_PREPEND') && WFWAF_AUTO_PREPEND && (!defined('WFWAF_SUBDIRECTORY_INSTALL') || !WFWAF_SUBDIRECTORY_INSTALL), 'deactivate' => array_key_exists('wf_deactivate', $_GET) ) )->render(); } public static function showDisabledApplicationPasswordsMessage() { echo wfView::create('user/disabled-application-passwords', array('isAdmin' => self::isCurrentUserAdmin()))->render(); } public static function _pluginPageActionLinks($links) { if (!wfConfig::get('isPaid')) { $links = array_merge(array('aWordfencePluginCallout' => '' . esc_html__('Upgrade To Premium', 'wordfence') . ' (' . esc_html__('opens in new tab', 'wordfence') . ')'), $links); } return $links; } public static function _outputLoginSecurityTour() { if (WORDFENCE_LS_FROM_CORE) { echo wfView::create('tours/login-security', array())->render(); } } public static function fixWPMailFromAddress($from_email) { if ($from_email == 'wordpress@') { //$_SERVER['SERVER_NAME'] is undefined so we get an incomplete email address wordfence::status(4, 'info', __("wp_mail from address is incomplete, attempting to fix", 'wordfence')); $urls = array(get_site_url(), get_home_url()); foreach ($urls as $u) { if (!empty($u)) { $u = preg_replace('#^[^/]*//+([^/]+).*$#', '\1', $u); if (substr($u, 0, 4) == 'www.') { $u = substr($u, 4); } if (!empty($u)) { wordfence::status(4, 'info', sprintf(/* translators: Email address. */ __("Fixing wp_mail from address: %s", 'wordfence'), $from_email . $u)); return $from_email . $u; } } } //Can't fix it, return it as it was } return $from_email; } public static function wpRedirectFilter($location, $status) { self::getLog()->initLogRequest(); self::getLog()->getCurrentRequest()->statusCode = $status; return $location; } public static function wpRedirectStatusFilter($status, $location) { self::getLog()->initLogRequest(); self::getLog()->getCurrentRequest()->statusCode = $status; self::getLog()->logHit(); return $status; } public static function enqueueAJAXWatcher() { $wafDisabled = !WFWAF_ENABLED || (class_exists('wfWAFConfig') && wfWAFConfig::isDisabled()); if (wfUtils::isAdmin() && !$wafDisabled) { wp_enqueue_style('wordfenceAJAXcss', wfUtils::getBaseURL() . wfUtils::versionedAsset('css/wordfenceBox.css'), '', WORDFENCE_VERSION); wp_enqueue_script('wfi18njs', wfUtils::getBaseURL() . wfUtils::versionedAsset('js/wfi18n.js'), array(), WORDFENCE_VERSION); wp_enqueue_script('wordfenceAJAXjs', wfUtils::getBaseURL() . wfUtils::versionedAsset('js/admin.ajaxWatcher.js'), array('jquery'), WORDFENCE_VERSION); wp_localize_script('wordfenceAJAXjs', 'WFAJAXWatcherVars', array( 'nonce' => wp_create_nonce('wf-waf-error-page'), )); self::setupI18nJSStrings(); } } private static function isWordfencePage($includeWfls = true) { $page = wfUtils::array_get($_GET, 'page', ''); return (!empty($page) && (preg_match('/^Wordfence/', $page) || ($includeWfls && $page == 'WFLS' && wfOnboardingController::shouldShowNewTour(wfOnboardingController::TOUR_LOGIN_SECURITY)))); } private static function isWordfenceSubpage($page, $subpage) { return array_key_exists('page', $_GET) && $_GET['page'] == ('Wordfence' . ucfirst($page)) && array_key_exists('subpage', $_GET) && $_GET['subpage'] == $subpage; } public static function enqueueDashboard() { if (wfUtils::isAdmin()) { wp_enqueue_style('wf-adminbar', wfUtils::getBaseURL() . wfUtils::versionedAsset('css/wf-adminbar.css'), '', WORDFENCE_VERSION); wp_enqueue_style('wordfence-license-global-style', wfLicense::current()->getGlobalStylesheet(), '', WORDFENCE_VERSION); wp_enqueue_script('wordfenceDashboardjs', wfUtils::getBaseURL() . wfUtils::versionedAsset('js/wfdashboard.js'), array('jquery'), WORDFENCE_VERSION); if (wfConfig::get('showAdminBarMenu')) { wp_enqueue_script('wordfencePopoverjs', wfUtils::getBaseURL() . wfUtils::versionedAsset('js/wfpopover.js'), array('jquery'), WORDFENCE_VERSION); wp_localize_script('wordfenceDashboardjs', 'WFDashVars', array( 'ajaxURL' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wp-ajax'), )); } } } public static function ajax_testAjax_callback(){ die("WFSCANTESTOK"); } public static function ajax_doScan_callback(){ @ignore_user_abort(true); self::$wordfence_wp_version = false; if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); } //This is messy, but not sure of a better way to do this without guaranteeing we get $wp_version require(ABSPATH . 'wp-includes/version.php'); /** @var string $wp_version */ self::$wordfence_wp_version = $wp_version; require_once(dirname(__FILE__) . '/wfScan.php'); wfScan::wfScanMain(); } //END doScan public static function ajax_lh_callback(){ self::getLog()->canLogHit = false; $UA = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; $isCrawler = empty($UA); if ($UA) { if (wfCrawl::isCrawler($UA) || wfCrawl::isGoogleCrawler()) { $isCrawler = true; } } @ob_end_clean(); if(! headers_sent()){ header('Content-type: text/javascript'); header("Connection: close"); header("Content-Length: 0"); header("X-Robots-Tag: noindex"); if (!$isCrawler) { wfLog::cacheHumanRequester(wfUtils::getIP(), $UA); } } flush(); if (!$isCrawler && array_key_exists('hid', $_GET)) { $hid = $_GET['hid']; $hid = wfUtils::decrypt($hid); if (!is_string($hid) || !preg_match('/^\d+$/', $hid)) { exit(); } $db = new wfDB(); $table_wfHits = wfDB::networkTable('wfHits'); $db->queryWrite("update {$table_wfHits} set jsRun=1 where id=%d", $hid); } die(""); } public static function ajaxReceiver(){ if(! wfUtils::isAdmin()){ wfUtils::send_json(array('errorMsg' => __("You appear to have logged out or you are not an admin. Please sign-out and sign-in again.", 'wordfence'))); } $func = (isset($_POST['action']) && $_POST['action']) ? $_POST['action'] : $_GET['action']; $nonce = (isset($_POST['nonce']) && $_POST['nonce']) ? $_POST['nonce'] : $_GET['nonce']; if(! wp_verify_nonce($nonce, 'wp-ajax')){ wfUtils::send_json(array('errorMsg' => __("Your browser sent an invalid security token to Wordfence. Please try reloading this page or signing out and in again.", 'wordfence'), 'tokenInvalid' => 1)); } //func is e.g. wordfence_ticker so need to munge it $func = str_replace('wordfence_', '', $func); $returnArr = call_user_func('wordfence::ajax_' . $func . '_callback'); if($returnArr === false){ $returnArr = array('errorMsg' => __("Wordfence encountered an internal error executing that request.", 'wordfence')); } if(! is_array($returnArr)){ error_log("Function " . wp_kses($func, array()) . " did not return an array and did not generate an error."); $returnArr = array(); } if(isset($returnArr['nonce'])){ error_log("Wordfence ajax function return an array with 'nonce' already set. This could be a bug."); } $returnArr['nonce'] = wp_create_nonce('wp-ajax'); wfUtils::send_json($returnArr); } public static function ajax_remoteVerifySwitchTo2FANew_callback() { $payload = wfUtils::decodeJWT(wfConfig::get('new2FAMigrationNonce')); if (empty($payload)) { wfUtils::send_json(new stdClass()); //Ensures an object response } $package = wfCrypt::noc1_encrypt($payload); wfUtils::send_json($package); } public static function ajax_switchTo2FANew_callback() { $migrate = (isset($_POST['migrate']) && wfUtils::truthyToBoolean($_POST['migrate'])); $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array()); if ($migrate && is_array($twoFactorUsers) && !empty($twoFactorUsers)) { $smsActive = array(); $authenticatorActive = array(); foreach ($twoFactorUsers as &$t) { if ($t[3] == 'activated') { $user = new WP_User($t[0]); if ($user instanceof WP_User && $user->exists()) { if ((!isset($t[5]) || $t[5] != 'authenticator')) { $smsActive[] = $user->user_login; } else { $authenticatorActive[] = $t[6]; } } } } if (!empty($smsActive)) { return array('ok' => 0, 'smsActive' => $smsActive); } $total = 0; $imported = 0; $nonce = bin2hex(wfWAFUtils::random_bytes(32)); wfConfig::set('new2FAMigrationNonce', wfUtils::generateJWT(array('nonce' => $nonce), 90)); $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion()); try { $response = $api->call('twoFactorTOTP_migrate', array(), array('migrateids' => json_encode($authenticatorActive), 'nonce' => $nonce, 'verifyurl' => add_query_arg(array('action' => 'wordfence_remoteVerifySwitchTo2FANew'), admin_url('admin-ajax.php')))); /* * A successful response will be in the format * { * "ok": 1, * "records": { * "skipped": { * : true, ... if applicable * }, * "totp": { * : { * "secret": , * "recovery": , * "ctime": , * "vtime": * }, * ... * } * } * } */ if (!is_array($response) || !isset($response['records']) || !is_array($response['records'])) { return array('ok' => 0, 'fail' => 1); } $secrets = $response['records']; if (!isset($secrets['totp']) || !is_array($secrets['totp'])) { return array('ok' => 0, 'fail' => 2); } $import = array(); foreach ($twoFactorUsers as &$t) { if ($t[3] == 'activated') { $user = new WP_User($t[0]); if ($user instanceof WP_User && $user->exists()) { if ((!isset($t[5]) || $t[5] != 'authenticator')) { //Do nothing } else { if (isset($secrets['totp'][$t[6]])) { $import[$user->ID] = $secrets['totp'][$t[6]]; $import[$user->ID]['type'] = 'authenticator'; $total++; } } } } } $imported = WFLSPHP52Compatability::import_2fa($import); } catch (Exception $e) { wordfence::status(4, 'error', sprintf(/* translators: Error message. */ __('2FA Migration Error: %s', 'wordfence'), $e->getMessage())); return array('ok' => 0, 'fail' => 1); } wfConfig::remove('new2FAMigrationNonce'); wfConfig::set(wfCredentialsController::DISABLE_LEGACY_2FA_OPTION, true); return array('ok' => 1, 'total' => $total, 'imported' => $imported); } //No legacy 2FA active, just set the option. wfConfig::set(wfCredentialsController::DISABLE_LEGACY_2FA_OPTION, true); return array('ok' => 1); } public static function ajax_switchTo2FAOld_callback() { wfConfig::set(wfCredentialsController::DISABLE_LEGACY_2FA_OPTION, false); return array('ok' => 1); } public static function validateProfileUpdate($errors, $update, $userData){ wordfence::validatePassword($errors, $userData); } public static function validatePassword($errors, $userData) { $password = (isset($_POST['pass1']) && trim($_POST['pass1'])) ? $_POST['pass1'] : false; $user_id = isset($userData->ID) ? $userData->ID : false; $username = isset($_POST["user_login"]) ? $_POST["user_login"] : $userData->user_login; if ($password == false) { return $errors; } if ($errors->get_error_data("pass")) { return $errors; } $enforceStrongPasswds = false; if (wfConfig::get('loginSec_strongPasswds_enabled')) { if (wfConfig::get('loginSec_strongPasswds') == 'pubs') { if (user_can($user_id, 'publish_posts')) { $enforceStrongPasswds = true; } } else if (wfConfig::get('loginSec_strongPasswds') == 'all') { $enforceStrongPasswds = true; } } if ($enforceStrongPasswds && !wordfence::isStrongPasswd($password, $username)) { $errors->add('pass', __('ERROR: The password could not be changed. Please choose a stronger password and try again. A strong password will follow these guidelines:
  • At least 12 characters
  • Uppercase and lowercase letters
  • At least one symbol
  • At least one number
  • Avoid common words or sequences of letters/numbers
', 'wordfence')); return $errors; } $twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array()); if (preg_match(self::$passwordCodePattern, $password) && is_array($twoFactorUsers) && count($twoFactorUsers) > 0) { $errors->add('pass', __('Passwords containing a space followed by "wf" without quotes are not allowed.', 'wordfence')); return $errors; } $enforceBreachedPasswds = false; if (wfConfig::get('loginSec_breachPasswds_enabled')) { if ($user_id !== false && wfConfig::get('loginSec_breachPasswds') == 'admins' && wfUtils::isAdmin($user_id)) { $enforceBreachedPasswds = true; } else if ($user_id !== false && wfConfig::get('loginSec_breachPasswds') == 'pubs' && user_can($user_id, 'publish_posts')) { $enforceBreachedPasswds = true; } } if ($enforceBreachedPasswds && wfCredentialsController::isLeakedPassword($username, $password)) { $errors->add('pass', sprintf(/* translators: Support URL. */ __('Please choose a different password. 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. Learn More', 'wordfence'), wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD))); return $errors; } else if ($user_id !== false) { wfAdminNoticeQueue::removeAdminNotice(false, '2faBreachPassword', array($user_id)); wfAdminNoticeQueue::removeAdminNotice(false, 'previousIPBreachPassword', array($user_id)); wfCredentialsController::clearCachedCredentialStatus($userData); } return $errors; } public static function isStrongPasswd($passwd, $username ) { $passwd = trim($passwd); $lowerPasswd = strtolower($passwd); $passwdLength = strlen($lowerPasswd); if ($passwdLength < 12) return false; if ($lowerPasswd == strtolower( $username ) ) return false; if (preg_match('/(?:password|passwd|mypass|wordpress)/i', $passwd)) return false; if (preg_match('/(.)\1{2,}/', $lowerPasswd)) //Disallow any character repeated 3 or more times return false; /* * Check for ordered sequences of at least 4 characters for alphabetic sequences and 3 characters for other sequences, ignoring case * Examples: * - 321 * - abcd * - abab */ $last = null; $sequenceLength = 1; $alphabetic = true; for ($i = 0; $i < $passwdLength; $i++) { $current = ord($lowerPasswd[$i]); if ($last !== null) { if (abs($current - $last) === 1) { $alphabetic &= ctype_alpha($lowerPasswd[$i]); if (++$sequenceLength > ($alphabetic ? 3 : 2)) return false; } else { $sequenceLength = 1; $alphabetic = true; } } $last = $current; } $characterTypes = array( '/[a-z]/', '/[A-Z]/', '/[0-9]/', '/[^a-zA-Z0-9]/' ); foreach ($characterTypes as $type) { if (!preg_match($type, $passwd)) return false; } return true; } public static function lostPasswordPost($errors = null, $user = null) { $IP = wfUtils::getIP(); if ($request = self::getLog()->getCurrentRequest()) { $request->action = 'lostPassword'; $request->save(); } if (wfBlock::isWhitelisted($IP)) { return; } $lockout = wfBlock::lockoutForIP(wfUtils::getIP()); if ($lockout !== false) { $lockout->recordBlock(); $customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', ''))); require(dirname(__FILE__) . '/wfLockedOut.php'); } if ($user === null) { if (empty($_POST['user_login'])) { return; } $user_login = $_POST['user_login']; if (is_array($user_login)) { $user_login = wfUtils::array_first($user_login); } $user_login = trim($user_login); $user = get_user_by('login', $user_login); if (!$user) { $user = get_user_by('email', $user_login); } } if ($user === false && wfConfig::get('loginSec_maskLoginErrors')) { if (self::hasWoocommerce() && isset($_POST['wc_reset_password'], $_POST['user_login'])) { $redirectUrl = add_query_arg('reset-link-sent', 'true', wc_get_account_endpoint_url('lost-password')); } else { $redirectUrl = !empty($_REQUEST['redirect_to']) ? $_REQUEST['redirect_to'] : 'wp-login.php?checkemail=confirm'; } wp_safe_redirect($redirectUrl); exit; } if($user){ $alertCallback = array(new wfLostPasswdFormAlert($user, wfUtils::getIP()), 'send'); do_action('wordfence_security_event', 'lostPasswdForm', array( 'email' => $user->user_email, 'ip' => wfUtils::getIP(), ), $alertCallback); } // do not count password reset attempts if there is a user logged in with the edit_users capability // because they're probably using the "send password reset" feature in the WP admin and therefore we shouldn't // be locking them out! if(wfConfig::get('loginSecurityEnabled') && !current_user_can( 'edit_users' ) ){ $tKey = self::getForgotPasswordFailureCountTransient($IP); $forgotAttempts = get_transient($tKey); if($forgotAttempts){ $forgotAttempts++; } else { $forgotAttempts = 1; } if($forgotAttempts >= wfConfig::get('loginSec_maxForgotPasswd')){ self::lockOutIP($IP, sprintf( /* translators: 1. Password reset limit (number). 2. WordPress username. */ __('Exceeded the maximum number of tries to recover their password which is set at: %1$s. The last username or email they entered before getting locked out was: \'%2$s\'', 'wordfence'), wfConfig::get('loginSec_maxForgotPasswd'), $_POST['user_login'] )); $customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', ''))); require(dirname(__FILE__) . '/wfLockedOut.php'); } set_transient($tKey, $forgotAttempts, wfConfig::get('loginSec_countFailMins') * 60); } } public static function lockOutIP($IP, $reason) { wfBlock::createLockout($reason, $IP, wfBlock::lockoutDuration(), time(), time(), 1); self::getLog()->tagRequestForLockout($reason); $alertCallback = array(new wfLoginLockoutAlert($IP, $reason), 'send'); do_action('wordfence_security_event', 'loginLockout', array( 'ip' => $IP, 'reason' => $reason, 'duration' => wfBlock::lockoutDuration(), ), $alertCallback); } public static function getLoginFailureCountTransient($IP) { return 'wflginfl_' . bin2hex(wfUtils::inet_pton($IP)); } public static function getForgotPasswordFailureCountTransient($IP) { return 'wffgt_' . bin2hex(wfUtils::inet_pton($IP)); } public static function clearLockoutCounters($IP) { delete_transient(self::getLoginFailureCountTransient($IP)); delete_transient(self::getForgotPasswordFailureCountTransient($IP)); } public static function veryFirstAction() { /** @var wpdb $wpdb ; */ global $wpdb; self::initProtection(); $wfFunc = wfUtils::array_get($_GET, '_wfsf'); if ($wfFunc == 'unlockEmail') { $nonceValid = false; if (isset($_POST['nonce']) && is_string($_POST['nonce'])) { $nonceValid = wp_verify_nonce($_POST['nonce'], 'wf-form'); if (!$nonceValid && method_exists(wfWAF::getInstance(), 'createNonce')) { $nonceValid = wfWAF::getInstance()->verifyNonce($_POST['nonce'], 'wf-form'); } } if(!$nonceValid){ die(__("Sorry but your browser sent an invalid security token when trying to use this form.", 'wordfence')); } $numTries = get_transient('wordfenceUnlockTries'); if($numTries > 10){ printf("

%s

%s

", esc_html__('Please wait 3 minutes and try again', 'wordfence'), esc_html__('You have used this form too much. Please wait 3 minutes and try again.', 'wordfence') ); exit(); } if(! $numTries){ $numTries = 1; } else { $numTries = $numTries + 1; } set_transient('wordfenceUnlockTries', $numTries, 180); $email = trim(@$_POST['email']); global $wpdb; $ws = $wpdb->get_results($wpdb->prepare("SELECT ID, user_login FROM $wpdb->users WHERE user_email = %s", $email)); $found = false; foreach($ws as $user){ $userDat = get_userdata($user->ID); if(wfUtils::isAdmin($userDat)){ if($email == $userDat->user_email){ $found = true; break; } } } if(! $found){ foreach(wfConfig::getAlertEmails() as $alertEmail){ if($alertEmail == $email){ $found = true; break; } } } if($found){ $key = wfUtils::bigRandomHex(); $IP = wfUtils::getIP(); set_transient('wfunlock_' . $key, $IP, 1800); $content = wfUtils::tmpl('email_unlockRequest.php', array( 'siteName' => get_bloginfo('name', 'raw'), 'siteURL' => wfUtils::getSiteBaseURL(), 'unlockHref' => wfUtils::getSiteBaseURL() . '?_wfsf=unlockAccess&key=' . $key, 'key' => $key, 'IP' => $IP )); wp_mail($email, __("Unlock email requested", 'wordfence'), $content, "Content-Type: text/html"); } echo "

" . esc_html__('Your request was received', 'wordfence') . "

" . esc_html(sprintf(/* translators: Email address. */ __("We received a request to email \"%s\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, they have been emailed instructions on how to regain access to this system. The instructions we sent will expire 30 minutes from now.", 'wordfence'), wp_kses($email, array()))) . "

"; exit(); } else if($wfFunc == 'unlockAccess'){ if (!preg_match('/^(?:(?:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9](?::|$)){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))$/i', get_transient('wfunlock_' . $_GET['key']))) { _e("Invalid key provided for authentication.", 'wordfence'); exit(); } if($_GET['func'] == 'unlockMyIP'){ wfBlock::unblockIP(wfUtils::getIP()); if (class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings(); } self::clearLockoutCounters(wfUtils::getIP()); header('Location: ' . wp_login_url()); exit(); } else if($_GET['func'] == 'unlockAllIPs'){ wordfence::status(1, 'info', __("Request received via unlock email link to unblock all IPs.", 'wordfence')); wfBlock::removeAllIPBlocks(); if (class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings(); } self::clearLockoutCounters(wfUtils::getIP()); header('Location: ' . wp_login_url()); exit(); } else if($_GET['func'] == 'disableRules'){ wfConfig::set('firewallEnabled', 0); wfConfig::set('loginSecurityEnabled', 0); wordfence::status(1, 'info', __("Request received via unlock email link to unblock all IPs via disabling firewall rules.", 'wordfence')); wfBlock::removeAllIPBlocks(); wfBlock::removeAllCountryBlocks(); if (class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings(); } self::clearLockoutCounters(wfUtils::getIP()); header('Location: ' . wp_login_url()); exit(); } else { _e("Invalid function specified. Please check the link we emailed you and make sure it was not cut-off by your email reader.", 'wordfence'); exit(); } } else if ($wfFunc == 'detectProxy') { wfUtils::doNotCache(); if (wfUtils::processDetectProxyCallback()) { self::getLog()->getCurrentRequest()->action = 'scan:detectproxy'; //Exempt a valid callback from live traffic echo wfConfig::get('detectProxyRecommendation', '-'); } else { echo '0'; } exit(); } else if ($wfFunc == 'removeAlertEmail') { wfUtils::doNotCache(); $payloadStatus = false; $jwt = (isset($_GET['jwt']) && is_string($_GET['jwt'])) ? $_GET['jwt'] : ''; if (!empty($jwt)) { $payload = wfUtils::decodeJWT($jwt); if ($payload && isset($payload['email'])) { $payloadStatus = true; } } if (isset($_POST['resend'])) { $email = trim(wfUtils::array_get($_POST, 'email', '')); $found = false; $alertEmails = wfConfig::getAlertEmails(); foreach ($alertEmails as $e) { if ($e == $email) { $found = true; break; } } if ($found) { $content = wfUtils::tmpl('email_unsubscribeRequest.php', array( 'siteName' => get_bloginfo('name', 'raw'), 'siteURL' => wfUtils::getSiteBaseURL(), 'IP' => wfUtils::getIP(), 'jwt' => wfUtils::generateJWT(array('email' => $email)), )); wp_mail($email, __("Unsubscribe Requested", 'wordfence'), $content, "Content-Type: text/html"); } echo wfView::create('common/unsubscribe', array( 'state' => 'resent', ))->render(); exit(); } else if (!$payloadStatus) { echo wfView::create('common/unsubscribe', array( 'state' => 'bad', ))->render(); exit(); } else if (isset($_POST['confirm'])) { $confirm = wfUtils::truthyToBoolean($_POST['confirm']); if ($confirm) { $found = false; $alertEmails = wfConfig::getAlertEmails(); $updatedAlertEmails = array(); foreach ($alertEmails as $alertEmail) { if ($alertEmail == $payload['email']) { $found = true; } else { $updatedAlertEmails[] = $alertEmail; } } if ($found) { wfConfig::set('alertEmails', implode(',', $updatedAlertEmails)); } echo wfView::create('common/unsubscribe', array( 'jwt' => $_GET['jwt'], 'email' => $payload['email'], 'state' => 'unsubscribed', ))->render(); exit(); } } echo wfView::create('common/unsubscribe', array( 'jwt' => $_GET['jwt'], 'email' => $payload['email'], 'state' => 'prompt', ))->render(); exit(); } else if ($wfFunc == 'installLicense') { if (wfUtils::isAdmin()) { wfUtils::doNotCache(); if (isset($_POST['license'])) { $nonceValid = wp_verify_nonce(@$_POST['nonce'], 'wf-form'); if (!$nonceValid) { die(__('Sorry but your browser sent an invalid security token when trying to use this form.', 'wordfence')); } $changes = array('apiKey' => $_POST['license']); $errors = wfConfig::validate($changes); if ($errors !== true) { $error = __('An error occurred while saving the license.', 'wordfence'); if (count($errors) == 1) { $error = sprintf(/* translators: Error message. */ __('An error occurred while saving the license: %s', 'wordfence'), $errors[0]['error']); } echo wfView::create('common/license', array( 'state' => 'bad', 'error' => $error, ))->render(); exit(); } try { wfConfig::save(wfConfig::clean($changes)); echo wfView::create('common/license', array( 'state' => 'installed', ))->render(); exit(); } catch (Exception $e) { echo wfView::create('common/license', array( 'state' => 'bad', 'error' => sprintf(/* translators: Error message. */ __('An error occurred while saving the license: %s', 'wordfence'), $e->getMessage()), ))->render(); exit(); } } echo wfView::create('common/license', array( 'state' => 'prompt', ))->render(); exit(); } } if (is_main_site() && wfUtils::isAdmin()) { if (wp_next_scheduled('wordfence_daily_cron') === false) { wp_schedule_event(time() + 600, 'daily', 'wordfence_daily_cron'); wordfence::status(2, 'info', __("Rescheduled missing daily cron", 'wordfence')); } if (wp_next_scheduled('wordfence_hourly_cron') === false) { wp_schedule_event(time() + 600, 'hourly', 'wordfence_hourly_cron'); wordfence::status(2, 'info', __("Rescheduled missing hourly cron", 'wordfence')); } } // Sync the WAF data with the database. if (!WFWAF_SUBDIRECTORY_INSTALL && $waf = wfWAF::getInstance()) { $homeurl = wfUtils::wpHomeURL(); $siteurl = wfUtils::wpSiteURL(); //Sync the GeoIP database if needed $destination = WFWAF_LOG_PATH . '/GeoLite2-Country.mmdb'; if (!file_exists($destination) || wfConfig::get('needsGeoIPSync')) { $allowSync = false; if (wfConfig::createLock('wfSyncGeoIP')) { $status = get_transient('wfSyncGeoIPActive'); if (!$status) { $allowSync = true; set_transient('wfSyncGeoIPActive', true, 3600); } wfConfig::releaseLock('wfSyncGeoIP'); } if ($allowSync) { wfUtils::requireIpLocator(); try { $wflogsLocator = wfIpLocator::getInstance(wfIpLocator::SOURCE_WFLOGS); $bundledLocator = wfIpLocator::getInstance(wfIpLocator::SOURCE_BUNDLED); if (!$wflogsLocator->isPreferred() || $wflogsLocator->getDatabaseVersion() !== $bundledLocator->getDatabaseVersion()) { $source = dirname(__FILE__) . '/GeoLite2-Country.mmdb'; if (copy($source, $destination)) { $shash = ''; $dhash = ''; $sp = @fopen($source, "rb"); if ($sp) { $scontext = hash_init('sha256'); while (!feof($sp)) { $data = fread($sp, 65536); if ($data === false) { $scontext = false; break; } hash_update($scontext, $data); } fclose($sp); if ($scontext !== false) { $shash = hash_final($scontext, false); } } $dp = @fopen($destination, "rb"); if ($dp) { $dcontext = hash_init('sha256'); while (!feof($dp)) { $data = fread($dp, 65536); if ($data === false) { $dcontext = false; break; } hash_update($dcontext, $data); } fclose($dp); if ($scontext !== false) { $dhash = hash_final($dcontext, false); } } if (hash_equals($shash, $dhash)) { wfConfig::remove('needsGeoIPSync'); delete_transient('wfSyncGeoIPActive'); } } } else { wfConfig::remove('needsGeoIPSync'); delete_transient('wfSyncGeoIPActive'); } } catch (Exception $e) { //Ignore } } } try { $sapi = @php_sapi_name(); if ($sapi != "cli") { $lastPermissionsTemplateCheck = wfConfig::getInt('lastPermissionsTemplateCheck', 0); if (defined('WFWAF_LOG_PATH') && ($lastPermissionsTemplateCheck + 43200) < time()) { //Run no more frequently than every 12 hours $timestamp = preg_replace('/[^0-9]/', '', microtime(false)); //We avoid using tmpfile since it can potentially create one with different permissions than the defaults $tmpTemplate = rtrim(WFWAF_LOG_PATH, '/') . "/template.{$timestamp}.tmp"; $template = rtrim(WFWAF_LOG_PATH, '/') . '/template.php'; @unlink($tmpTemplate); @file_put_contents($tmpTemplate, "Access denied