/home/vianto5/.trash/cititower.mx/wp-content/plugins/wordfence/lib
Edit: /home/vianto5/.trash/cititower.mx/wp-content/plugins/wordfence/lib/wordfenceClass.php (451606B)
=')) {
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(){
//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);
//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();
wfCentral::populateCentralSiteUrl();
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 http://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 http://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();
$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) {
$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 {
self::_refreshUpdateNotification();
}
}
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 (function_exists('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) {
@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');
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();
}
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) {
return (isset($_GET['page']) && (preg_match('/^Wordfence/', @$_GET['page']) || ($includeWfls && $_GET['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 = isset($_GET['_wfsf']) ? @$_GET['_wfsf'] : false;
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(@$_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, "\n");
$tmpStat = @stat($tmpTemplate);
if ($tmpStat !== false) {
$mode = $tmpStat[2] & 0777;
$updatedMode = 0600;
if (($mode & 0020) == 0020) { //Group writable
$updatedMode = $updatedMode | 0060;
}
if (defined('WFWAF_LOG_FILE_MODE')) {
$updatedMode = WFWAF_LOG_FILE_MODE;
}
$stat = @stat($template);
if ($stat === false || ($stat[2] & 0777) != $updatedMode) {
@chmod($tmpTemplate, $updatedMode);
@unlink($template);
@rename($tmpTemplate, $template);
}
@unlink($tmpTemplate);
}
else {
@unlink($tmpTemplate);
}
wfConfig::set('lastPermissionsTemplateCheck', time());
@chmod(WFWAF_LOG_PATH, (wfWAFWordPress::permissions() | 0755));
wfWAFWordPress::writeHtaccess();
$contents = self::_wflogsContents();
if ($contents) {
$validFiles = wfWAF::getInstance()->fileList();
foreach ($validFiles as &$vf) {
$vf = basename($vf);
}
$validFiles = array_filter($validFiles);
$previousWflogsFileList = wfConfig::getJSON('previousWflogsFileList', array());
$wflogs = realpath(WFWAF_LOG_PATH);
$filesRemoved = array();
foreach ($contents as $f) {
if (!in_array($f, $validFiles) && in_array($f, $previousWflogsFileList)) {
$fullPath = $f;
$removed = self::_recursivelyRemoveWflogs($f);
$filesRemoved = array_merge($filesRemoved, $removed);
}
}
$contents = self::_wflogsContents();
wfConfig::setJSON('previousWflogsFileList', $contents);
if (!empty($filesRemoved)) {
$removalHistory = wfConfig::getJSON('diagnosticsWflogsRemovalHistory', array());
$removalHistory = array_slice($removalHistory, 0, 4);
array_unshift($removalHistory, array(time(), $filesRemoved));
wfConfig::setJSON('diagnosticsWflogsRemovalHistory', $removalHistory);
}
}
}
}
}
catch (Exception $e) {
//Ignore
}
try {
$configDefaults = array(
'apiKey' => wfConfig::get('apiKey'),
'isPaid' => !!wfConfig::get('isPaid'),
'siteURL' => $siteurl,
'homeURL' => $homeurl,
'whitelistedIPs' => (string) wfConfig::get('whitelisted'),
'whitelistedServiceIPs' => @json_encode(wfUtils::whitelistedServiceIPs()),
'howGetIPs' => (string) wfConfig::get('howGetIPs'),
'howGetIPs_trusted_proxies_unified' => implode("\n", wfUtils::unifiedTrustedProxies()),
'detectProxyRecommendation' => (string) wfConfig::get('detectProxyRecommendation'),
'other_WFNet' => !!wfConfig::get('other_WFNet', true),
'pluginABSPATH' => ABSPATH,
'serverIPs' => json_encode(wfUtils::serverIPs()),
'blockCustomText' => wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', ''))),
'disableWAFIPBlocking' => wfConfig::get('disableWAFIPBlocking'),
'wordpressVersion' => wfConfig::get('wordpressVersion'),
'wordpressPluginVersions' => wfConfig::get_ser('wordpressPluginVersions'),
'wordpressThemeVersions' => wfConfig::get_ser('wordpressThemeVersions'),
'WPLANG' => get_site_option('WPLANG'),
);
if (wfUtils::isAdmin()) {
$errorNonceKey = 'errorNonce_' . get_current_user_id();
$configDefaults[$errorNonceKey] = wp_create_nonce('wf-waf-error-page'); //Used by the AJAX watcher script
}
foreach ($configDefaults as $key => $value) {
$waf->getStorageEngine()->setConfig($key, $value, 'synced');
}
if (wfConfig::get('timeoffset_wf') !== false) {
$waf->getStorageEngine()->setConfig('timeoffset_wf', wfConfig::get('timeoffset_wf'), 'synced');
}
else {
$waf->getStorageEngine()->unsetConfig('timeoffset_wf', 'synced');
}
if (class_exists('wfWAFIPBlocksController')) {
wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
}
if (wfUtils::isAdmin()) {
if ($waf->getStorageEngine()->getConfig('wafStatus', '') == 'learning-mode') {
if ($waf->getStorageEngine()->getConfig('learningModeGracePeriodEnabled', false)) {
if ($waf->getStorageEngine()->getConfig('learningModeGracePeriod', 0) <= time()) {
// Reached the end of the grace period, activate the WAF.
$waf->getStorageEngine()->setConfig('wafStatus', 'enabled');
$waf->getStorageEngine()->setConfig('learningModeGracePeriodEnabled', 0);
$waf->getStorageEngine()->unsetConfig('learningModeGracePeriod');
$firewall = new wfFirewall();
$firewall->syncStatus(true);
}
}
}
}
if (empty($_GET['wordfence_syncAttackData'])) {
$table_wfHits = wfDB::networkTable('wfHits');
if ($waf->getStorageEngine() instanceof wfWAFStorageMySQL) {
$lastAttackMicroseconds = floatval($waf->getStorageEngine()->getConfig('lastAttackDataTruncateTime'));
} else {
$lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$table_wfHits}");
}
if (get_site_option('wordfence_lastSyncAttackData', 0) < time() - 8) {
if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
if (get_site_option('wordfence_syncingAttackData') <= time() - 60) {
// Could be the request to itself is not completing, add ajax to the head as a workaround
$attempts = get_site_option('wordfence_syncAttackDataAttempts', 0);
if ($attempts > 10) {
add_action('wp_head', 'wordfence::addSyncAttackDataAjax');
add_action('login_head', 'wordfence::addSyncAttackDataAjax');
add_action('admin_head', 'wordfence::addSyncAttackDataAjax');
} else {
update_site_option('wordfence_syncAttackDataAttempts', ++$attempts);
wp_remote_post(add_query_arg('wordfence_syncAttackData', microtime(true), home_url('/')), array(
'timeout' => 0.01,
'blocking' => false,
'sslverify' => apply_filters('https_local_ssl_verify', false)
));
}
}
}
}
}
if ($waf instanceof wfWAFWordPress && ($learningModeAttackException = $waf->getLearningModeAttackException())) {
$log = self::getLog();
$log->initLogRequest();
$request = $log->getCurrentRequest();
$request->action = 'learned:waf';
$request->attackLogTime = microtime(true);
$ruleIDs = array();
/** @var wfWAFRule $failedRule */
foreach ($learningModeAttackException->getFailedRules() as $failedRule) {
$ruleIDs[] = $failedRule->getRuleID();
}
$actionData = array(
'learningMode' => 1,
'failedRules' => $ruleIDs,
'paramKey' => $learningModeAttackException->getParamKey(),
'paramValue' => $learningModeAttackException->getParamValue(),
);
if ($ruleIDs && $ruleIDs[0]) {
$rule = $waf->getRule($ruleIDs[0]);
if ($rule) {
$request->actionDescription = $rule->getDescription();
$actionData['category'] = $rule->getCategory();
$actionData['ssl'] = $waf->getRequest()->getProtocol() === 'https';
$actionData['fullRequest'] = base64_encode($waf->getRequest());
}
}
$request->actionData = wfRequestModel::serializeActionData($actionData);
register_shutdown_function(array($request, 'save'));
self::scheduleSendAttackData();
}
} catch (wfWAFStorageFileException $e) {
// We don't have anywhere to write files in this scenario.
} catch (wfWAFStorageEngineMySQLiException $e) {
// Ignore and continue
}
}
if(wfConfig::get('firewallEnabled')){
$wfLog = self::getLog();
$wfLog->firewallBadIPs();
$IP = wfUtils::getIP();
if (wfBlock::isWhitelisted($IP)) {
return;
}
if (wfConfig::get('neverBlockBG') == 'neverBlockUA' && wfCrawl::isGoogleCrawler()) {
return;
}
if (wfConfig::get('neverBlockBG') == 'neverBlockVerified' && wfCrawl::isVerifiedGoogleCrawler()) {
return;
}
if (wfConfig::get('bannedURLs', false)) {
$URLs = explode("\n", wfUtils::cleanupOneEntryPerLine(wfConfig::get('bannedURLs')));
foreach ($URLs as $URL) {
if (preg_match(wfUtils::patternToRegex($URL, ''), $_SERVER['REQUEST_URI'])) {
$reason = __('Accessed a banned URL', 'wordfence');
wfBlock::createIP($reason, $IP, wfBlock::blockDuration(), time(), time(), 1, wfBlock::TYPE_IP_AUTOMATIC_TEMPORARY);
wfActivityReport::logBlockedIP($IP, null, 'bannedurl');
$wfLog->tagRequestForBlock($reason);
$wfLog->do503(3600, __("Accessed a banned URL", 'wordfence'));
//exits
}
}
}
if (wfConfig::get('other_blockBadPOST') == '1' && $_SERVER['REQUEST_METHOD'] == 'POST' && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER'])) {
$reason = __('POST received with blank user-agent and referer', 'wordfence');
wfBlock::createIP($reason, $IP, wfBlock::blockDuration(), time(), time(), 1, wfBlock::TYPE_IP_AUTOMATIC_TEMPORARY);
wfActivityReport::logBlockedIP($IP, null, 'badpost');
$wfLog->tagRequestForBlock($reason);
$wfLog->do503(3600, __("POST received with blank user-agent and referer", 'wordfence'));
//exits
}
}
}
private static function _wflogsContents() {
$dir = opendir(WFWAF_LOG_PATH);
if ($dir) {
$contents = array();
while ($path = readdir($dir)) {
if ($path == '.' || $path == '..') { continue; }
$contents[] = $path;
}
closedir($dir);
return $contents;
}
return false;
}
/**
* Removes a path within wflogs, recursing as necessary.
*
* @param string $file
* @param array $processedDirs
* @return array The list of removed files/folders.
*/
private static function _recursivelyRemoveWflogs($file, $processedDirs = array()) {
if (preg_match('~(?:^|/|\\\\)\.\.(?:/|\\\\|$)~', $file)) {
return array();
}
if (stripos(WFWAF_LOG_PATH, 'wflogs') === false) { //Sanity check -- if not in a wflogs folder, user will have to do removal manually
return array();
}
$path = rtrim(WFWAF_LOG_PATH, '/') . '/' . $file;
if (is_link($path)) {
if (@unlink($path)) {
return array($file);
}
return array();
}
if (is_dir($path)) {
$real = realpath($file);
if (in_array($real, $processedDirs)) {
return array();
}
$processedDirs[] = $real;
$count = 0;
$dir = opendir($path);
if ($dir) {
$contents = array();
while ($sub = readdir($dir)) {
if ($sub == '.' || $sub == '..') { continue; }
$contents[] = $sub;
}
closedir($dir);
$filesRemoved = array();
foreach ($contents as $f) {
$removed = self::_recursivelyRemoveWflogs($file . '/' . $f, $processedDirs);
$filesRemoved = array($filesRemoved, $removed);
}
}
if (@rmdir($path)) {
$filesRemoved[] = $file;
}
return $filesRemoved;
}
if (@unlink($path)) {
return array($file);
}
return array();
}
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(
'userID' => $userID,
'username' => $username,
'firstName' => $user->first_name,
'lastName' => $user->last_name,
'time' => wfUtils::localHumanDateShort(),
'IP' => wfUtils::getIP()
));
}
$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
if($newDevice){
$newDevice = !(isset($_COOKIE[$cookieName]) && hash_equals($cookieValue, $_COOKIE[$cookieName]));
}
else{
$_COOKIE[$cookieName]=$cookieValue;
}
if(wfUtils::isAdmin($userID)){
$securityEvent = 'adminLogin';
$alertCallback = array(new wfAdminLoginAlert($cookieName, $cookieValue, $username, wfUtils::getIP()), 'send');
} else {
$securityEvent = 'nonAdminLogin';
$alertCallback = array(new wfNonAdminLoginAlert($cookieName, $cookieValue, $username, wfUtils::getIP()), 'send');
}
if($newDevice)
$securityEvent.='NewLocation';
do_action('wordfence_security_event', $securityEvent, array(
'username' => $username,
'ip' => wfUtils::getIP(),
), $alertCallback);
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', __('ERROR : You can\'t register using that username', 'wordfence'));
}
return $errors;
}
public static function wooRegistrationFilter($wooCustomerData) {
/*
$wooCustomerData matches:
array(
'user_login' => $username,
'user_pass' => $password,
'user_email' => $email,
'role' => 'customer',
)
*/
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
}
return $wooCustomerData;
}
public static function oembedAuthorFilter($data, $post, $width, $height) {
unset($data['author_name']);
unset($data['author_url']);
return $data;
}
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)) {
$id = (int) $matches[1];
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); }
}
}
}
return $response;
}
public static function jsonAPIAdjustHeaders($response, $server, $request) {
if (defined('WORDFENCE_REST_API_SUPPRESSED')) {
$response->header('Allow', 'GET');
}
return $response;
}
public static function wpSitemapUserProviderFilter($provider, $name) {
if ($name === 'users') {
return false;
}
return $provider;
}
public static function _filterCentralFromLiveTraffic($dispatch_result, $request, $route, $handler) {
if (preg_match('~^/wordfence/v\d+/~i', $route)) {
self::getLog()->canLogHit = false;
}
return $dispatch_result;
}
public static function showTwoFactorField() {
$existingContents = ob_get_contents();
if (!preg_match('/wftwofactornonce:([0-9]+)\/(.+?)\s/', $existingContents, $matches)) {
return;
}
$userID = intval($matches[1]);
$twoFactorNonce = preg_replace('/[^a-f0-9]/i', '', $matches[2]);
if (!self::verifyTwoFactorIntermediateValues($userID, $twoFactorNonce)) {
return;
}
//Strip out the username and password fields
$formPosition = strrpos($existingContents, '', $formPosition);
if ($formPosition === false || $formTagEnd === false) {
return;
}
ob_end_clean();
ob_start();
echo substr($existingContents, 0, $formTagEnd + 1);
//Add the 2FA field
echo "
Authentication Code
";
}
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);
}
}
return false;
}
public static function authenticateFilter($authUser, $username, $passwd) {
wfConfig::inc('totalLoginHits'); //The total hits to wp-login.php including logins, logouts and just hits.
$IP = wfUtils::getIP();
$secEnabled = wfConfig::get('loginSecurityEnabled');
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array());
$userDat = self::$userDat;
$checkBreachList = $secEnabled &&
!wfBlock::isWhitelisted($IP) &&
wfConfig::get('loginSec_breachPasswds_enabled') &&
is_object($authUser) &&
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;
if ($checkBreachList) {
$cacheStatus = wfCredentialsController::cachedCredentialStatus($authUser);
if ($cacheStatus != wfCredentialsController::UNCACHED) {
$usingBreachedPassword = ($cacheStatus == wfCredentialsController::LEAKED);
}
else {
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 &&
is_object($userDat) &&
get_class($userDat) == 'WP_User' &&
wfCredentialsController::useLegacy2FA();
if ($checkTwoFactor) {
$twoFactorRecord = false;
$hasActivatedTwoFactorUser = false;
foreach ($twoFactorUsers as &$t) {
if ($t[3] == 'activated') {
$userID = $t[0];
$testUser = get_user_by('ID', $userID);
if (is_object($testUser) && wfUtils::isAdmin($testUser)) {
$hasActivatedTwoFactorUser = true;
}
if ($userID == $userDat->ID) {
$twoFactorRecord = &$t;
}
}
}
if (isset($_POST['wordfence_authFactor']) && $_POST['wordfence_authFactor'] && $twoFactorRecord) { //User authenticated with name and password, 2FA code ready to check
$userID = $userDat->ID;
if (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(__('VERIFICATION FAILED : 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(__('ERROR : The username or password you entered is incorrect. Lost your password ?', '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. */
__('WARNING: 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 change your password . Learn More (' . esc_html__('opens in new tab', 'wordfence') . ') ', '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());
try {
$codeResult = $api->call('twoFactorTOTP_verify', array(), array('totpid' => $twoFactorRecord[6], 'code' => $code, 'mode' => $mode));
if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
//No longer a paid key, let them sign in without two factor
}
else if (isset($codeResult['ok']) && $codeResult['ok']) {
//Everything's good, let the sign in continue
}
else {
if (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(__('INVALID CODE : Please sign in again and add a space, the letters wf, and the code from your authenticator app to the end of your password (e.g., wf123456).', 'wordfence'), array('strong'=>array(), 'code'=>array())));
}
else {
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(__('INVALID CODE : Please sign in again and add a space, the letters wf, and the code sent to your phone to the end of your password (e.g., wf123456).', 'wordfence'), array('strong'=>array(), 'code'=>array())));
}
}
else {
$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(__('AUTHENTICATION FAILURE : A temporary failure was encountered while trying to log in. Please try again.', 'wordfence'), array('strong'=>array())));
return self::$authError;
}
$loginNonce = bin2hex($loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
if ($mode == 'authenticator') {
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(__('INVALID CODE : 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())) . '');
}
else {
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(__('INVALID CODE : 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())) . '');
}
}
return self::processBruteForceAttempt(self::$authError, $username, $passwd);
}
}
catch (Exception $e) {
if (self::isDebugOn()) {
error_log('TOTP validation error: ' . $e->getMessage());
}
} // Couldn't connect to noc1, let them sign in since the password was correct.
}
else { //Old method phone authentication
$authFactor = $_POST['wordfence_authFactor'];
if (strlen($authFactor) == 4) {
$authFactor = 'wf' . $authFactor;
}
if ($authFactor == $twoFactorRecord[2] && $twoFactorRecord[4] > time()) { // Set this 2FA code to expire in 30 seconds (for other plugins hooking into the auth process)
$twoFactorRecord[4] = time() + 30;
wfConfig::set_ser('twoFactorUsers', $twoFactorUsers);
}
else if ($authFactor == $twoFactorRecord[2]) {
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
try {
$codeResult = $api->call('twoFactor_verification', array(), array('phone' => $twoFactorRecord[1]));
if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
//No longer a paid key, let them sign in without two factor
}
else if (isset($codeResult['ok']) && $codeResult['ok']) {
$twoFactorRecord[2] = $codeResult['code'];
$twoFactorRecord[4] = time() + 1800; //30 minutes until code expires
wfConfig::set_ser('twoFactorUsers', $twoFactorUsers); //save the code the user needs to enter and return an error.
$loginNonce = wfWAFUtils::random_bytes(20);
if ($loginNonce === false) { //Should never happen but is technically possible
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(__('AUTHENTICATION FAILURE : A temporary failure was encountered while trying to log in. Please try again.', 'wordfence'), array('strong'=>array())));
return self::$authError;
}
$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(__('CODE EXPIRED. CHECK YOUR PHONE: 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())) . '');
return self::$authError;
}
//else: No new code was received. Let them sign in with the expired code.
}
catch (Exception $e) {
// Couldn't connect to noc1, let them sign in since the password was correct.
}
}
else { //Bad code, so cancel the login and return an error to user.
$loginNonce = wfWAFUtils::random_bytes(20);
if ($loginNonce === false) { //Should never happen but is technically possible
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(__('AUTHENTICATION FAILURE : A temporary failure was encountered while trying to log in. Please try again.', 'wordfence'), array('strong'=>array())));
return self::$authError;
}
$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(__('INVALID CODE : 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())) . '');
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) {
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. */
__('WARNING: 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 change your password . Learn More (' . esc_html__('opens in new tab', 'wordfence') . ') ', '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;
}
else {
$loginNonce = bin2hex($loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonce', $loginNonce);
update_user_meta($userDat->ID, '_wf_twoFactorNonceTime', time());
if (isset($twoFactorRecord[5])) { //New method TOTP authentication
if ($twoFactorRecord[5] == 'authenticator') {
if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
$retries = get_option('limit_login_retries', array());
$ip = limit_login_get_address();
if (!is_array($retries)) {
$retries = array();
}
if (isset($retries[$ip]) && is_int($retries[$ip])) {
$retries[$ip]--;
}
else {
$retries[$ip] = 0;
}
update_option('limit_login_retries', $retries);
}
$allowSeparatePrompt = ini_get('output_buffering') > 0;
if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
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(__('CODE REQUIRED : Please check your authenticator app for the current code. Enter it below to sign in.', 'wordfence'), array('strong'=>array())) . '');
return self::$authError;
}
else {
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(__('CODE REQUIRED : Please check your authenticator app for the current code. Please sign in again and add a space, the letters wf, and the code to the end of your password (e.g., wf123456).', 'wordfence'), array('strong'=>array(), 'code'=>array())));
return self::$authError;
}
}
else {
//Phone TOTP
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
try {
$codeResult = $api->call('twoFactorTOTP_sms', array(), array('totpid' => $twoFactorRecord[6]));
if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
$requireAdminTwoFactor = false;
//Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up.
}
else {
if (isset($codeResult['ok']) && $codeResult['ok']) {
if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
$retries = get_option('limit_login_retries', array());
$ip = limit_login_get_address();
if (!is_array($retries)) {
$retries = array();
}
if (isset($retries[$ip]) && is_int($retries[$ip])) {
$retries[$ip]--;
}
else {
$retries[$ip] = 0;
}
update_option('limit_login_retries', $retries);
}
$allowSeparatePrompt = ini_get('output_buffering') > 0;
if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
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(__('CHECK YOUR PHONE : A code has been sent to your phone and will arrive within 30 seconds. Enter it below to sign in.', 'wordfence'), array('strong'=>array())) . '');
return self::$authError;
}
else {
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(__('CHECK YOUR PHONE : A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space, the letters wf, and the code to the end of your password (e.g., wf123456).', 'wordfence'), array('strong'=>array(), 'code'=>array())));
return self::$authError;
}
}
else { //oops, our API returned an error.
$requireAdminTwoFactor = false;
//Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
}
}
}
catch (Exception $e) {
if (self::isDebugOn()) {
error_log('TOTP SMS error: ' . $e->getMessage());
}
$requireAdminTwoFactor = false;
// Couldn't connect to noc1, let them sign in since the password was correct.
}
}
}
else { //Old method phone authentication
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
try {
$codeResult = $api->call('twoFactor_verification', array(), array('phone' => $twoFactorRecord[1]));
if (isset($codeResult['notPaid']) && $codeResult['notPaid']) {
$requireAdminTwoFactor = false;
//Let them sign in without two factor if their API key has expired or they're not paid and for some reason they have this set up.
}
else {
if (isset($codeResult['ok']) && $codeResult['ok']) {
$twoFactorRecord[2] = $codeResult['code'];
$twoFactorRecord[4] = time() + 1800; //30 minutes until code expires
wfConfig::set_ser('twoFactorUsers', $twoFactorUsers); //save the code the user needs to enter and return an error.
if (self::hasGDLimitLoginsMUPlugin() && function_exists('limit_login_get_address')) {
$retries = get_option('limit_login_retries', array());
$ip = limit_login_get_address();
if (!is_array($retries)) {
$retries = array();
}
if (isset($retries[$ip]) && is_int($retries[$ip])) {
$retries[$ip]--;
}
else {
$retries[$ip] = 0;
}
update_option('limit_login_retries', $retries);
}
$allowSeparatePrompt = ini_get('output_buffering') > 0;
if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
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(__('CHECK YOUR PHONE : A code has been sent to your phone and will arrive within 30 seconds. Enter it below to sign in.', 'wordfence'), array('strong'=>array())) . '');
return self::$authError;
}
else {
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(__('CHECK YOUR PHONE : A code has been sent to your phone and will arrive within 30 seconds. Please sign in again and add a space and the code to the end of your password (e.g., wfABCD).', 'wordfence'), array('strong'=>array(), 'code'=>array())));
return self::$authError;
}
}
else { //oops, our API returned an error.
$requireAdminTwoFactor = false;
//Let them sign in without two factor because the API is broken and we don't want to lock users out of their own systems.
}
}
}
catch (Exception $e) {
$requireAdminTwoFactor = false;
// Couldn't connect to noc1, let them sign in since the password was correct.
}
} //end: Old method phone authentication
}
}
}
else if ($usingBreachedPassword) {
if (wfCredentialsController::hasPreviousLoginFromIP($authUser, wfUtils::getIP())) {
wfAdminNoticeQueue::removeAdminNotice(false, '2faBreachPassword', array($authUser->ID));
wfAdminNoticeQueue::addAdminNotice(wfAdminNotice::SEVERITY_CRITICAL, sprintf(__('WARNING: Your login has been allowed because you have previously logged in from the same IP, but you will be blocked if your IP changes. 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 change your password . Learn More (' . esc_html__('opens in new tab', 'wordfence') . ') ', 'wordfence'), self_admin_url('profile.php'), wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD)), 'previousIPBreachPassword', array($authUser->ID));
}
else {
$username = $authUser->user_login;
self::getLog()->logLogin('loginFailValidUsername', 1, $username);
$alertCallback = array(new wfBreachLoginAlert($username, wp_lostpassword_url(), wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD), wfUtils::getIP()), 'send');
do_action('wordfence_security_event', 'breachLogin', array(
'username' => $username,
'resetPasswordURL' => wp_lostpassword_url(),
'supportURL' => wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD),
'ip' => wfUtils::getIP(),
), $alertCallback);
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('breached_password', sprintf(
/* translators: 1. Reset password URL. 2. Support URL. */
wp_kses(__('INSECURE PASSWORD: Your login attempt has been blocked because 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 reset your password to reactivate your account. Learn More (opens in new tab) ', 'wordfence'), array('strong'=>array(), 'a'=>array('href'=>array(), 'target'=>array(), 'rel'=>array()), 'span'=>array('style'=>array()))), wp_lostpassword_url(), wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD)));
return self::$authError;
}
}
if ($requireAdminTwoFactor && wfUtils::isAdmin($authUser)) {
$username = $authUser->user_login;
self::getLog()->logLogin('loginFailValidUsername', 1, $username);
wordfence::alert(__("Admin Login Blocked", 'wordfence'), sprintf(/* translators: WordPress username. */__("A user with username \"%s\" who has administrator access tried to sign in to your WordPress site. Access was denied because all administrator accounts are required to have Cellphone Sign-in enabled but this account does not.", 'wordfence'), $username), wfUtils::getIP());
self::$authError = new WP_Error('twofactor_disabled_required', wp_kses(__('Cellphone Sign-in Required : Cellphone Sign-in is required for all administrator accounts. Please contact the site administrator to enable it for your account.', 'wordfence'), array('strong'=>array())));
return self::$authError;
}
//User is not configured for two factor. Sign in without two factor.
}
} //End: if ($checkTwoFactor)
else if ($usingBreachedPassword) {
if (wfCredentialsController::hasPreviousLoginFromIP($authUser, wfUtils::getIP())) {
wfAdminNoticeQueue::removeAdminNotice(false, '2faBreachPassword', array($authUser->ID));
wfAdminNoticeQueue::addAdminNotice(wfAdminNotice::SEVERITY_CRITICAL, sprintf(/* translators: 1. Reset password URL. 2. Support URL. */ __('WARNING: Your login has been allowed because you have previously logged in from the same IP, but you will be blocked if your IP changes. 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 change your password . Learn More (' . esc_html__('opens in new tab', 'wordfence') . ') ', 'wordfence'), self_admin_url('profile.php'), wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD)), 'previousIPBreachPassword', array($authUser->ID));
}
else {
$username = $authUser->user_login;
self::getLog()->logLogin('loginFailValidUsername', 1, $username);
$alertCallback = array(new wfBreachLoginAlert($username, wp_lostpassword_url(), wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD), wfUtils::getIP()), 'send');
do_action('wordfence_security_event', 'breachLogin', array(
'username' => $username,
'resetPasswordURL' => wp_lostpassword_url(),
'supportURL' => wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD),
'ip' => wfUtils::getIP(),
), $alertCallback);
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('breached_password', sprintf(
/* translators: 1. Reset password URL. 2. Support URL. */
wp_kses(__('INSECURE PASSWORD: Your login attempt has been blocked because 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 reset your password to reactivate your account. Learn More (opens in new tab) ', 'wordfence'), array('strong'=>array(), 'a'=>array('href'=>array(), 'target'=>array(), 'rel'=>array()), 'span'=>array('style'=>array()))), wp_lostpassword_url(), wfSupportController::esc_supportURL(wfSupportController::ITEM_USING_BREACH_PASSWORD)));
return self::$authError;
}
}
return self::processBruteForceAttempt($authUser, $username, $passwd);
}
public static function checkSecurityNetwork($endpointType = null) {
if (wfConfig::get('other_WFNet')) {
$IP = wfUtils::getIP();
if ($maxBlockTime = self::wfsnIsBlocked($IP, 'brute', $endpointType)) {
$secsToGo = ($maxBlockTime ? $maxBlockTime : wfBlock::blockDuration());
$reason = __('Blocked by Wordfence Security Network', 'wordfence');
wfBlock::createWFSN($reason, $IP, $secsToGo, time(), time(), 1);
wfActivityReport::logBlockedIP($IP, null, 'brute');
self::getLog()->tagRequestForBlock($reason, true);
self::getLog()->getCurrentRequest()->action = 'blocked:wfsn';
self::getLog()->do503($secsToGo, $reason); //exits
}
}
}
public static function processBruteForceAttempt($authUser, $username, $passwd) {
$IP = wfUtils::getIP();
$secEnabled = wfConfig::get('loginSecurityEnabled');
if (wfBlock::isWhitelisted($IP)) {
return $authUser;
}
$failureErrorCodes = array('invalid_username', 'invalid_email', 'incorrect_password', 'twofactor_invalid', 'authentication_failed', 'wfls_twofactor_invalid', 'wfls_twofactor_failed', 'wfls_twofactor_blocked');
if (is_wp_error($authUser) && in_array($authUser->get_error_code(), $failureErrorCodes)) {
self::checkSecurityNetwork(); //May exit
}
if($secEnabled){
if(is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'invalid_email')){
if($blacklist = wfConfig::get('loginSec_userBlacklist')){
$users = explode("\n", wfUtils::cleanupOneEntryPerLine($blacklist));
foreach($users as $user){
if(strtolower($username) == strtolower($user)){
$secsToGo = wfBlock::blockDuration();
$reason = __('Blocked by login security setting', 'wordfence');
wfBlock::createIP($reason, $IP, $secsToGo, time(), time(), 1, wfBlock::TYPE_IP_AUTOMATIC_TEMPORARY);
wfActivityReport::logBlockedIP($IP, null, 'brute');
self::getLog()->tagRequestForBlock($reason);
self::getLog()->do503($secsToGo, $reason); //exits
}
}
}
if(wfConfig::get('loginSec_lockInvalidUsers')){
if(strlen($username) > 0 && preg_match('/[^\r\s\n\t]+/', $username)){
self::lockOutIP($IP, sprintf(/* translators: WordPress username. */ __("Used an invalid username '%s' to try to sign in", 'wordfence'), $username));
self::getLog()->logLogin('loginFailInvalidUsername', true, $username);
}
$customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', '')));
require(dirname(__FILE__) . '/wfLockedOut.php');
}
}
$tKey = self::getLoginFailureCountTransient($IP);
if(is_wp_error($authUser) && in_array($authUser->get_error_code(), $failureErrorCodes)) {
$tries = get_transient($tKey);
if($tries){
$tries++;
} else {
$tries = 1;
}
if($tries >= wfConfig::get('loginSec_maxFailures')){
self::lockOutIP($IP,
sprintf(
/* translators: 1. Login attempt limit. 2. WordPress username. */
__('Exceeded the maximum number of login failures which is: %1$s. The last username they tried to sign in with was: \'%2$s\'', 'wordfence'),
wfConfig::get('loginSec_maxFailures'),
$username
)
);
$customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', '')));
require(dirname(__FILE__) . '/wfLockedOut.php');
}
set_transient($tKey, $tries, wfConfig::get('loginSec_countFailMins') * 60);
}
}
if(is_wp_error($authUser)){
if($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'invalid_email'){
self::getLog()->logLogin('loginFailInvalidUsername', 1, $username);
} else {
self::getLog()->logLogin('loginFailValidUsername', 1, $username);
}
}
if(is_wp_error($authUser) && ($authUser->get_error_code() == 'invalid_username' || $authUser->get_error_code() == 'invalid_email' || $authUser->get_error_code() == 'incorrect_password') && wfConfig::get('loginSec_maskLoginErrors')){
return new WP_Error( 'incorrect_password', sprintf(
/* translators: 1. WordPress username. 2. Reset password URL. */
wp_kses(__( 'ERROR : The username or password you entered is incorrect. Lost your password ?', 'wordfence' ), array('strong'=>array(), 'a'=>array('href'=>array(), 'title'=>array()))), $username, wp_lostpassword_url() ) );
}
return $authUser;
}
public static function wfsnBatchReportBlockedAttempts() {
if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
$threshold = wfConfig::get('lastBruteForceDataSendTime', 0);;
$wfdb = new wfDB();
$table_wfHits = wfDB::networkTable('wfHits');
$rawBlocks = $wfdb->querySelect("SELECT IP, ctime, actionData FROM {$table_wfHits} WHERE ctime > %f AND action = 'blocked:wfsnrepeat' ORDER BY ctime ASC LIMIT 100", sprintf('%.6f', $threshold));
$totalRows = $wfdb->querySingle("SELECT COUNT(*) FROM {$table_wfHits} WHERE ctime > %f AND action = 'blocked:wfsnrepeat'", sprintf('%.6f', $threshold));
$ipCounts = array();
$maxctime = 0;
foreach ($rawBlocks as $record) {
$maxctime = max($maxctime, $record['ctime']);
$endpointType = 0;
if (!empty($record['actionData'])) {
$actionData = wfRequestModel::unserializeActionData($record['actionData']);
if (isset($actionData['type'])) {
$endpointType = $actionData['type'];
}
}
if (isset($ipCounts[$record['IP']])) {
$ipCounts[$record['IP']] = array();
}
if (isset($ipCounts[$record['IP']][$endpointType])) {
$ipCounts[$record['IP']][$endpointType]++;
}
else {
$ipCounts[$record['IP']][$endpointType] = 1;
}
}
$toSend = array();
foreach ($ipCounts as $IP => $endpoints) {
foreach ($endpoints as $endpointType => $count) {
$toSend[] = array('IP' => base64_encode($IP), 'count' => $count, 'blocked' => 1, 'type' => $endpointType);
}
}
try {
$response = wp_remote_post(WORDFENCE_HACKATTEMPT_URL_SEC . 'multipleHackAttempts/?k=' . rawurlencode(wfConfig::get('apiKey')) . '&t=brute', array(
'timeout' => 2,
'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'),
'body' => 'IPs=' . rawurlencode(json_encode($toSend)),
'headers' => array('Referer' => false),
));
if (!is_wp_error($response)) {
if ($totalRows > 100) {
self::wfsnScheduleBatchReportBlockedAttempts();
}
wfConfig::set('lastBruteForceDataSendTime', $maxctime);
}
else {
self::wfsnScheduleBatchReportBlockedAttempts();
}
}
catch (Exception $err) {
//Do nothing
}
}
private static function wfsnScheduleBatchReportBlockedAttempts($timeToSend = null) {
if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
if ($timeToSend === null) {
$timeToSend = time() + 30;
}
$notMainSite = is_multisite() && !is_main_site();
if ($notMainSite) {
global $current_site;
switch_to_blog($current_site->blog_id);
}
if (!wp_next_scheduled('wordfence_batchReportBlockedAttempts')) {
wp_schedule_single_event($timeToSend, 'wordfence_batchReportBlockedAttempts');
}
if ($notMainSite) {
restore_current_blog();
}
}
public static function wfsnReportBlockedAttempt($IP, $type){
if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
self::wfsnScheduleBatchReportBlockedAttempts();
$endpointType = self::wfsnEndpointType();
self::getLog()->getCurrentRequest()->actionData = wfRequestModel::serializeActionData(array('type' => $endpointType));
}
public static function wfsnBatchReportFailedAttempts() {
if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
$threshold = time();
$wfdb = new wfDB();
$table_wfSNIPCache = wfDB::networkTable('wfSNIPCache');
$rawRecords = $wfdb->querySelect("SELECT id, IP, type, count, 1 AS failed FROM {$table_wfSNIPCache} WHERE count > 0 AND expiration < FROM_UNIXTIME(%d) LIMIT 100", $threshold);
$toSend = array();
$toDelete = array();
if (count($rawRecords)) {
foreach ($rawRecords as $record) {
$toDelete[] = $record['id'];
unset($record['id']);
$record['IP'] = base64_encode(filter_var($record['IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? wfUtils::inet_aton($record['IP']) : wfUtils::inet_pton($record['IP']));
$key = $record['IP'] . $record['type']; //Aggregate multiple records if for some reason there are multiple for an IP/type combination
if (!isset($toSend[$key])) {
$toSend[$key] = $record;
}
else {
$toSend[$key]['count'] += $record['count'];
}
}
$toSend = array_values($toSend);
try {
$response = wp_remote_post(WORDFENCE_HACKATTEMPT_URL_SEC . 'multipleHackAttempts/?k=' . rawurlencode(wfConfig::get('apiKey')) . '&t=brute', array(
'timeout' => 2,
'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'),
'body' => 'IPs=' . rawurlencode(json_encode($toSend)),
'headers' => array('Referer' => false),
));
if (is_wp_error($response)) {
self::wfsnScheduleBatchReportFailedAttempts();
return;
}
}
catch (Exception $err) {
//Do nothing
}
}
array_unshift($toDelete, $threshold);
$wfdb->queryWriteIgnoreError("DELETE FROM {$table_wfSNIPCache} WHERE (expiration < FROM_UNIXTIME(%d) AND count = 0)" . (count($toDelete) > 1 ? " OR id IN (" . rtrim(str_repeat('%d, ', count($toDelete) - 1), ', ') . ")" : ""), $toDelete);
$remainingRows = $wfdb->querySingle("SELECT COUNT(*) FROM {$table_wfSNIPCache}");
if ($remainingRows > 0) {
self::wfsnScheduleBatchReportFailedAttempts();
}
}
private static function wfsnScheduleBatchReportFailedAttempts($timeToSend = null) {
if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
if ($timeToSend === null) {
$timeToSend = time() + 30;
}
$notMainSite = is_multisite() && !is_main_site();
if ($notMainSite) {
global $current_site;
switch_to_blog($current_site->blog_id);
}
if (!wp_next_scheduled('wordfence_batchReportFailedAttempts')) {
wp_schedule_single_event($timeToSend, 'wordfence_batchReportFailedAttempts');
}
if ($notMainSite) {
restore_current_blog();
}
}
public static function wfsnIsBlocked($IP, $hitType, $endpointType = null) {
if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
$wfdb = new wfDB();
if ($endpointType === null) { $endpointType = self::wfsnEndpointType(); }
$table_wfSNIPCache = wfDB::networkTable('wfSNIPCache');
$cachedRecord = $wfdb->querySingleRec("SELECT id, body FROM {$table_wfSNIPCache} WHERE IP = '%s' AND type = %d AND expiration > NOW()", $IP, $endpointType);
if (isset($cachedRecord)) {
$wfdb->queryWriteIgnoreError("UPDATE {$table_wfSNIPCache} SET count = count + 1 WHERE id = %d", $cachedRecord['id']);
if (preg_match('/BLOCKED:(\d+)/', $cachedRecord['body'], $matches) && (!wfBlock::isWhitelisted($IP))) {
return $matches[1];
}
return false;
}
$backoff = get_transient('wfsn_backoff');
if ($backoff) {
return false;
}
try {
$result = wp_remote_get(WORDFENCE_HACKATTEMPT_URL_SEC . 'hackAttempt/?k=' . rawurlencode(wfConfig::get('apiKey')) .
'&IP=' . rawurlencode(filter_var($IP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? wfUtils::inet_aton($IP) : wfUtils::inet_pton($IP)) .
'&t=' . rawurlencode($hitType) .
'&type=' . $endpointType,
array(
'timeout' => 3,
'user-agent' => "Wordfence.com UA " . (defined('WORDFENCE_VERSION') ? WORDFENCE_VERSION : '[Unknown version]'),
'headers' => array('Referer' => false),
));
if (is_wp_error($result)) {
set_transient('wfsn_backoff', 1, WORDFENCE_NOC3_FAILED_BACKOFF_TIME);
return false;
}
$wfdb->queryWriteIgnoreError("INSERT INTO {$table_wfSNIPCache} (IP, type, expiration, body) VALUES ('%s', %d, DATE_ADD(NOW(), INTERVAL %d SECOND), '%s')", $IP, $endpointType, 30, $result['body']);
self::wfsnScheduleBatchReportFailedAttempts();
if (preg_match('/BLOCKED:(\d+)/', $result['body'], $matches) && (!wfBlock::isWhitelisted($IP))) {
return $matches[1];
}
return false;
} catch (Exception $err) {
set_transient('wfsn_backoff', 1, WORDFENCE_NOC3_FAILED_BACKOFF_TIME);
return false;
}
}
public static function wfsnEndpointType() {
$type = 0; //Unknown
if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) {
$type = 2;
}
else if (defined('DOING_AJAX') && DOING_AJAX) {
$type = 3;
if (isset($_REQUEST['action']) && ($_REQUEST['action'] == 'wordfence_ls_authenticate' || $_REQUEST['action'] == 'nopriv_wordfence_ls_authenticate')) {
$type = 301;
}
}
else if (strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false) {
$type = 1;
}
return $type;
}
public static function logoutAction(){
$userID = self::getLog()->getCurrentRequest()->userID;
$userDat = get_user_by('id', $userID);
if(is_object($userDat)){
self::getLog()->logLogin('logout', 0, $userDat->user_login);
}
// Unset the roadblock cookie
if (!WFWAF_SUBDIRECTORY_INSTALL) {
wfUtils::setcookie(wfWAF::getInstance()->getAuthCookieName(), ' ', time() - (86400 * 365), '/', null, wfUtils::isFullSSL(), true);
}
}
public static function loginInitAction() {
$lockout = wfBlock::lockoutForIP(wfUtils::getIP());
if ($lockout !== false) {
$lockout->recordBlock();
$customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', '')));
require(dirname(__FILE__) . '/wfLockedOut.php');
}
self::doEarlyAccessLogging(); //Rate limiting
}
public static function authAction(&$username, &$passwd){
$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 (isset($_POST['wordfence_twoFactorUser'])) { //Final stage of login -- get and verify 2fa code, make sure we load the appropriate user
$userID = intval($_POST['wordfence_twoFactorUser']);
$twoFactorNonce = preg_replace('/[^a-f0-9]/i', '', $_POST['wordfence_twoFactorNonce']);
if (self::verifyTwoFactorIntermediateValues($userID, $twoFactorNonce)) {
$user = get_user_by('ID', $userID);
$username = $user->user_login;
$passwd = $twoFactorNonce;
self::$userDat = $user;
return;
}
}
if (is_array($username) || is_array($passwd)) { return; }
//Intermediate stage of login
if(! $username){ return; }
$userDat = get_user_by('login', $username);
if (!$userDat) {
$userDat = get_user_by('email', $username);
}
self::$userDat = $userDat;
if(preg_match(self::$passwordCodePattern, $passwd, $matches)){
$_POST['wordfence_authFactor'] = $matches[1];
$passwd = preg_replace('/^(.+)\s+wf([a-z0-9 ]+)$/i', '$1', $passwd);
$_POST['pwd'] = $passwd;
}
}
public static function authUserAction($user, $password) {
$lockout = wfBlock::lockoutForIP(wfUtils::getIP());
if ($lockout !== false) {
$lockout->recordBlock();
$customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', '')));
require(dirname(__FILE__) . '/wfLockedOut.php');
}
return $user;
}
public static function getWPFileContent($file, $cType, $cName, $cVersion){
if ($cType == 'plugin') {
if (preg_match('#^/?wp-content/plugins/[^/]+/#', $file)) {
$file = preg_replace('#^/?wp-content/plugins/[^/]+/#', '', $file);
}
else {
//If user is using non-standard wp-content dir, then use /plugins/ in pattern to figure out what to strip off
$file = preg_replace('#^.*[^/]+/plugins/[^/]+/#', '', $file);
}
}
else if ($cType == 'theme') {
if (preg_match('#/?wp-content/themes/[^/]+/#', $file)) {
$file = preg_replace('#/?wp-content/themes/[^/]+/#', '', $file);
}
else {
$file = preg_replace('#^.*[^/]+/themes/[^/]+/#', '', $file);
}
}
else if ($cType == 'core') {
//No special processing
}
else {
return array('errorMsg' => __('An invalid type was specified to get file.', 'wordfence'));
}
$api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
try {
$contResult = $api->binCall('get_wp_file_content', array(
'v' => wfUtils::getWPVersion(),
'file' => $file,
'cType' => $cType,
'cName' => $cName,
'cVersion' => $cVersion
));
if ($contResult['data']) {
return array('fileContent' => $contResult['data']);
}
throw new Exception(__('We could not fetch a core WordPress file from the Wordfence API.', 'wordfence'));
}
catch (Exception $e) {
return array('errorMsg' => wp_kses($e->getMessage(), array()));
}
}
public static function ajax_sendDiagnostic_callback(){
add_filter('gettext', 'wordfence::_diagnosticsTranslationDisabler', 0, 3);
$inEmail = true;
$body = "This email is the diagnostic from " . site_url() . ".\nThe IP address that requested this was: " . wfUtils::getIP() . "\nTicket Number/Forum Username: " . $_POST['ticket'];
$sendingDiagnosticEmail = true;
ob_start();
require(dirname(__FILE__) . '/menu_tools_diagnostic.php');
$body = nl2br($body) . ob_get_clean();
$findReplace = array(
'