public static function runInstall() { if (self::$runInstallCalled) { return; } self::$runInstallCalled = true; if (function_exists('ignore_user_abort')) { ignore_user_abort(true); } $previous_version = get_option('wordfence_version', '0.0.0'); update_option('wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install. //EVERYTHING HERE MUST BE IDEMPOTENT //Remove old legacy cron job if exists wp_clear_scheduled_hook('wordfence_scheduled_scan'); $schema = new wfSchema(); $schema->createAll(); //if not exists wfConfig::setDefaults(); //If not set $restOfSite = wfConfig::get('cbl_restOfSiteBlocked', 'notset'); if ($restOfSite == 'notset') { wfConfig::set('cbl_restOfSiteBlocked', '1'); } //Install new schedule. If schedule config is blank it will install the default 'auto' schedule. wordfence::scheduleScans(); if (wfConfig::get('autoUpdate') == '1') { wfConfig::enableAutoUpdate(); //Sets up the cron } if (!wfConfig::get('apiKey')) { $api = new wfAPI('', wfUtils::getWPVersion()); try { $keyData = $api->call('get_anon_api_key'); if ($keyData['ok'] && $keyData['apiKey']) { wfConfig::set('apiKey', $keyData['apiKey']); } else { throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key."); } } catch (Exception $e) { error_log("Could not fetch free API key from Wordfence: " . $e->getMessage()); return; } } wp_clear_scheduled_hook('wordfence_daily_cron'); wp_clear_scheduled_hook('wordfence_hourly_cron'); if (is_main_site()) { wp_schedule_event(time(), 'daily', 'wordfence_daily_cron'); //'daily' wp_schedule_event(time(), 'hourly', 'wordfence_hourly_cron'); } $db = new wfDB(); if ($db->columnExists('wfHits', 'HTTPHeaders')) { //Upgrade from 3.0.4 global $wpdb; $prefix = $wpdb->base_prefix; $count = $db->querySingle("select count(*) as cnt from {$prefix}" . "wfHits"); if ($count > 20000) { $db->queryWrite("delete from {$prefix}" . "wfHits order by id asc limit " . ($count - 20000)); } $db->dropColumn('wfHits', 'HTTPHeaders'); } //Upgrading from 1.5.6 or earlier needs: $db->createKeyIfNotExists('wfStatus', 'level', 'k2'); if (wfConfig::get('isPaid') == 'free') { wfConfig::set('isPaid', ''); } //End upgrade from 1.5.6 /** @var wpdb $wpdb */ global $wpdb; $prefix = $wpdb->base_prefix; $db->queryWriteIgnoreError("alter table {$prefix}" . "wfConfig modify column val longblob"); $db->queryWriteIgnoreError("alter table {$prefix}" . "wfBlocks add column permanent tinyint UNSIGNED default 0"); $db->queryWriteIgnoreError("alter table {$prefix}" . "wfStatus modify column msg varchar(1000) NOT NULL"); //3.1.2 to 3.1.4 $db->queryWriteIgnoreError("alter table {$prefix}" . "wfBlocks modify column blockedTime bigint signed NOT NULL"); //3.2.1 to 3.2.2 $db->queryWriteIgnoreError("alter table {$prefix}" . "wfLockedOut modify column blockedTime bigint signed NOT NULL"); $db->queryWriteIgnoreError("drop table if exists {$prefix}" . "wfFileQueue"); $db->queryWriteIgnoreError("drop table if exists {$prefix}" . "wfFileChanges"); $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}wfStatus where field = 'id'"); if (!$result || strtolower($result->Key) != 'pri') { //Adding primary key to this table because some backup apps use primary key during backup. $db->queryWriteIgnoreError("alter table {$prefix}wfStatus add id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY"); } $optScanEnabled = $db->querySingle("select val from {$prefix}" . "wfConfig where name='scansEnabled_options'"); if ($optScanEnabled != '0' && $optScanEnabled != '1') { $db->queryWrite("update {$prefix}" . "wfConfig set val='1' where name='scansEnabled_options'"); } $optScanEnabled = $db->querySingle("select val from {$prefix}" . "wfConfig where name='scansEnabled_heartbleed'"); if ($optScanEnabled != '0' && $optScanEnabled != '1') { //Enable heartbleed if no value is set. wfConfig::set('scansEnabled_heartbleed', 1); } if (wfConfig::get('cacheType') == 'php' || wfConfig::get('cacheType') == 'falcon') { wfCache::removeCacheDirectoryHtaccess(); } // IPv6 schema changes for 6.0.1 $tables_with_ips = array('wfCrawlers', 'wfBadLeechers', 'wfBlockedIPLog', 'wfBlocks', 'wfHits', 'wfLeechers', 'wfLockedOut', 'wfLocs', 'wfLogins', 'wfReverseCache', 'wfScanners', 'wfThrottleLog', 'wfVulnScanners'); foreach ($tables_with_ips as $ip_table) { $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}{$ip_table} where field = 'IP'"); if (!$result || strtolower($result->Type) == 'binary(16)') { continue; } $db->queryWriteIgnoreError("ALTER TABLE {$prefix}{$ip_table} 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 {$prefix}{$ip_table} where field = 'IP'"); if (!$result || strtolower($result->Type) != 'binary(16)') { continue; } $db->queryWriteIgnoreError("UPDATE {$prefix}{$ip_table} SET IP = CONCAT(LPAD(CHAR(0xff, 0xff), 12, CHAR(0)), LPAD(\n\tCHAR(\n\t\tCAST(IP as UNSIGNED) >> 24 & 0xFF,\n\t\tCAST(IP as UNSIGNED) >> 16 & 0xFF,\n\t\tCAST(IP as UNSIGNED) >> 8 & 0xFF,\n\t\tCAST(IP as UNSIGNED) & 0xFF\n\t),\n\t4,\n\tCHAR(0)\n))"); } // Fix the data in the country column. // TODO: add version check so this doesn't run on every update. $ip_results = $wpdb->get_results("SELECT * FROM `{$prefix}wfBlockedIPLog` GROUP BY IP"); if ($ip_results) { foreach ($ip_results as $ip_row) { $wpdb->query($wpdb->prepare("UPDATE `{$prefix}wfBlockedIPLog` SET countryCode = %s WHERE IP = %s", wfUtils::IP2Country(wfUtils::inet_ntop($ip_row->IP)), $ip_row->IP)); } } 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 = $wpdb->base_prefix . 'wfHits'; foreach ($colsFor610 as $col => $colDefintion) { $count = $wpdb->get_col($wpdb->prepare(<<<SQL SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND COLUMN_NAME=%s AND TABLE_NAME=%s SQL , $col, $hitTable)); if (!$count) { $wpdb->query("ALTER TABLE {$hitTable} ADD COLUMN {$colDefintion}"); } } $has404 = $wpdb->get_col($wpdb->prepare(<<<SQL SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND COLUMN_NAME='is404' AND TABLE_NAME=%s SQL , $hitTable)); if ($has404) { $wpdb->query(<<<SQL UPDATE {$hitTable} SET statusCode= CASE WHEN is404=1 THEN 404 ELSE 200 END SQL ); $wpdb->query("ALTER TABLE {$hitTable} DROP COLUMN `is404`"); } $loginsTable = "{$wpdb->base_prefix}wfLogins"; $hasHitID = $wpdb->get_col($wpdb->prepare(<<<SQL SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND COLUMN_NAME='hitID' AND TABLE_NAME=%s SQL , $loginsTable)); if (!$hasHitID) { $wpdb->query("ALTER TABLE {$loginsTable} ADD COLUMN hitID int(11) DEFAULT NULL AFTER `id`, ADD INDEX(hitID)"); } if (!WFWAF_SUBDIRECTORY_INSTALL) { try { wfWAF::getInstance()->getStorageEngine()->setConfig('wafDisabled', false); } catch (wfWAFStorageFileException $e) { error_log($e); } } // Call this before creating the index in cases where the wp-cron isn't running. self::trimWfHits(); $hitsTable = "{$wpdb->base_prefix}wfHits"; $hasAttackLogTimeIndex = $wpdb->get_var($wpdb->prepare(<<<SQL SELECT COLUMN_KEY FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = 'attackLogTime' SQL , $hitsTable)); if (!$hasAttackLogTimeIndex) { $wpdb->query("ALTER TABLE {$hitsTable} ADD INDEX `attackLogTime` (`attackLogTime`)"); } //Must be the final line }
/** * @param mixed $ip_address * @param int|null $unixday */ public static function logBlockedIP($ip_address, $unixday = null) { /** @var wpdb $wpdb */ global $wpdb; if (wfUtils::isValidIP($ip_address)) { $ip_bin = wfUtils::inet_pton($ip_address); } else { $ip_bin = $ip_address; $ip_address = wfUtils::inet_ntop($ip_bin); } $blocked_table = "{$wpdb->base_prefix}wfBlockedIPLog"; $unixday_insert = 'FLOOR(UNIX_TIMESTAMP() / 86400)'; if (is_int($unixday)) { $unixday_insert = absint($unixday); } $country = wfUtils::IP2Country($ip_address); $wpdb->query($wpdb->prepare(<<<SQL INSERT INTO {$blocked_table} (IP, countryCode, blockCount, unixday) VALUES (%s, %s, 1, {$unixday_insert}) ON DUPLICATE KEY UPDATE blockCount = blockCount + 1 SQL , $ip_bin, $country)); }
public function displayIP($binaryIP) { $readableIP = wfUtils::inet_ntop($binaryIP); $country = wfUtils::countryCode2Name(wfUtils::IP2Country($readableIP)); return "{$readableIP} (" . ($country ? $country : 'Unknown') . ")"; }
public function firewallBadIPs() { $IP = wfUtils::getIP(); if ($this->isWhitelisted($IP)) { return; } $IPnum = wfUtils::inet_pton($IP); //New range and UA pattern blocking: $r1 = $this->getDB()->querySelect("select id, blockType, blockString from " . $this->ipRangesTable); foreach ($r1 as $blockRec) { if ($blockRec['blockType'] == 'IU') { $ipRangeBlocked = false; $uaPatternBlocked = false; $refBlocked = false; $bDat = explode('|', $blockRec['blockString']); $ipRange = $bDat[0]; $uaPattern = $bDat[1]; $refPattern = isset($bDat[2]) ? $bDat[2] : ''; if ($ipRange) { list($start_range, $end_range) = explode('-', $ipRange); if (preg_match('/[\\.:]/', $start_range)) { $start_range = wfUtils::inet_pton($start_range); $end_range = wfUtils::inet_pton($end_range); } else { $start_range = wfUtils::inet_pton(long2ip($start_range)); $end_range = wfUtils::inet_pton(long2ip($end_range)); } if (strcmp($IPnum, $start_range) >= 0 && strcmp($IPnum, $end_range) <= 0) { $ipRangeBlocked = true; } } if ($uaPattern) { if (wfUtils::isUABlocked($uaPattern)) { $uaPatternBlocked = true; } } if ($refPattern) { if (wfUtils::isRefererBlocked($refPattern)) { $refBlocked = true; } } $doBlock = false; if ($uaPattern && $ipRange && $refPattern) { if ($uaPatternBlocked && $ipRangeBlocked && $refBlocked) { $doBlock = true; } } if ($uaPattern && $ipRange) { if ($uaPatternBlocked && $ipRangeBlocked) { $doBlock = true; } } if ($uaPattern && $refPattern) { if ($uaPatternBlocked && $refBlocked) { $doBlock = true; } } if ($ipRange && $refPattern) { if ($ipRangeBlocked && $refBlocked) { $doBlock = true; } } else { if ($uaPattern) { if ($uaPatternBlocked) { $doBlock = true; } } else { if ($ipRange) { if ($ipRangeBlocked) { $doBlock = true; } } else { if ($refPattern) { if ($refBlocked) { $doBlock = true; } } } } } if ($doBlock) { $this->getDB()->queryWrite("update " . $this->ipRangesTable . " set totalBlocked = totalBlocked + 1, lastBlocked = unix_timestamp() where id=%d", $blockRec['id']); wfActivityReport::logBlockedIP($IP); $this->do503(3600, "Advanced blocking in effect."); } } } //End range/UA blocking // Country blocking if (wfConfig::get('isPaid')) { $blockedCountries = wfConfig::get('cbl_countries', false); $bareRequestURI = wfUtils::extractBareURI($_SERVER['REQUEST_URI']); $bareBypassRedirURI = wfUtils::extractBareURI(wfConfig::get('cbl_bypassRedirURL', '')); $skipCountryBlocking = false; if ($bareBypassRedirURI && $bareRequestURI == $bareBypassRedirURI) { //Run this before country blocking because even if the user isn't blocked we need to set the bypass cookie so they can bypass future blocks. $bypassRedirDest = wfConfig::get('cbl_bypassRedirDest', ''); if ($bypassRedirDest) { self::setCBLCookieBypass(); $this->redirect($bypassRedirDest); //exits } } $bareBypassViewURI = wfUtils::extractBareURI(wfConfig::get('cbl_bypassViewURL', '')); if ($bareBypassViewURI && $bareBypassViewURI == $bareRequestURI) { self::setCBLCookieBypass(); $skipCountryBlocking = true; } if (!$skipCountryBlocking && $blockedCountries && !self::isCBLBypassCookieSet()) { if (is_user_logged_in() && !wfConfig::get('cbl_loggedInBlocked', false)) { //User is logged in and we're allowing logins //Do nothing } else { if (strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false && !wfConfig::get('cbl_loginFormBlocked', false)) { //It's the login form and we're allowing that //Do nothing } else { if (strpos($_SERVER['REQUEST_URI'], '/wp-login.php') === false && !wfConfig::get('cbl_restOfSiteBlocked', false)) { //It's the rest of the site and we're allowing that //Do nothing } else { if ($country = wfUtils::IP2Country($IP)) { foreach (explode(',', $blockedCountries) as $blocked) { if (strtoupper($blocked) == strtoupper($country)) { //At this point we know the user has been blocked if (wfConfig::get('cbl_action') == 'redir') { $redirURL = wfConfig::get('cbl_redirURL'); $eRedirHost = wfUtils::extractHostname($redirURL); $isExternalRedir = false; if ($eRedirHost && $eRedirHost != wfUtils::extractHostname(home_url())) { //It's an external redirect... $isExternalRedir = true; } if (!$isExternalRedir && wfUtils::extractBareURI($redirURL) == $bareRequestURI) { //Is this the URI we want to redirect to, then don't block it //Do nothing /* Uncomment the following if page components aren't loading for the page we redirect to. Uncommenting is not recommended because it means that anyone from a blocked country can crawl your site by sending the page blocked users are redirected to as the referer for every request. But it's your call. } else if(wfUtils::extractBareURI($_SERVER['HTTP_REFERER']) == $redirURL){ //If the referer the page we want to redirect to? Then this might be loading as a component so don't block. //Do nothing */ } else { $this->redirect(wfConfig::get('cbl_redirURL')); } } else { $this->do503(3600, "Access from your area has been temporarily limited for security reasons"); wfConfig::inc('totalCountryBlocked'); } } } } } } } } } if ($rec = $this->getDB()->querySingleRec("select blockedTime, reason from " . $this->blocksTable . " where IP=%s and (permanent=1 OR (blockedTime + %s > unix_timestamp()))", $IPnum, wfConfig::get('blockedTime'))) { $this->getDB()->queryWrite("update " . $this->blocksTable . " set lastAttempt=unix_timestamp(), blockedHits = blockedHits + 1 where IP=%s", $IPnum); $now = $this->getDB()->querySingle("select unix_timestamp()"); $secsToGo = $rec['blockedTime'] + wfConfig::get('blockedTime') - $now; if (wfConfig::get('other_WFNet') && strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false) { //We're on the login page and this IP has been blocked wordfence::wfsnReportBlockedAttempt($IP, 'login'); } $this->do503($secsToGo, $rec['reason']); } }
/** * */ public static function processAttackData() { global $wpdb; $waf = wfWAF::getInstance(); if ($waf->getStorageEngine()->getConfig('attackDataKey', false) === false) { $waf->getStorageEngine()->setConfig('attackDataKey', mt_rand(0, 0xfff)); } //Send alert email if needed if (wfConfig::get('wafAlertOnAttacks')) { $alertInterval = wfConfig::get('wafAlertInterval', 0); $cutoffTime = max(time() - $alertInterval, wfConfig::get('wafAlertLastSendTime')); $wafAlertWhitelist = wfConfig::get('wafAlertWhitelist', ''); $wafAlertWhitelist = preg_split("/[,\r\n]+/", $wafAlertWhitelist); foreach ($wafAlertWhitelist as $index => &$entry) { $entry = trim($entry); if (!preg_match('/^(?:\\d{1,3}(?:\\.|$)){4}/', $entry) && !preg_match('/^((?:[\\da-f]{1,4}(?::|)){0,8})(::)?((?:[\\da-f]{1,4}(?::|)){0,8})$/i', $entry)) { unset($wafAlertWhitelist[$index]); continue; } $packed = @wfUtils::inet_pton($entry); if ($packed === false) { unset($wafAlertWhitelist[$index]); continue; } $entry = bin2hex($packed); } $wafAlertWhitelist = array_filter($wafAlertWhitelist); $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits\n\tWHERE action = 'blocked:waf' " . (count($wafAlertWhitelist) ? "AND HEX(IP) NOT IN (" . implode(", ", array_fill(0, count($wafAlertWhitelist), '%s')) . ")" : "") . "AND attackLogTime > %.6f\n\tORDER BY attackLogTime DESC\n\tLIMIT 10", array_merge($wafAlertWhitelist, array($cutoffTime)))); $attackCount = $wpdb->get_var('SELECT FOUND_ROWS()'); if ($attackCount >= wfConfig::get('wafAlertThreshold')) { $durationMessage = wfUtils::makeDuration($alertInterval); $message = <<<ALERTMSG The Wordfence Web Application Firewall has blocked {$attackCount} attacks over the last {$durationMessage}. Below is a sample of these recent attacks: ALERTMSG; $attackTable = array(); $dateMax = $ipMax = $countryMax = 0; foreach ($attackData as $row) { $row->longDescription = "Blocked for " . $row->actionDescription; $actionData = json_decode($row->actionData, true); if (!is_array($actionData) || !isset($actionData['paramKey']) || !isset($actionData['paramValue'])) { continue; } $paramKey = base64_decode($actionData['paramKey']); $paramValue = base64_decode($actionData['paramValue']); if (strlen($paramValue) > 100) { $paramValue = substr($paramValue, 0, 100) . chr(2026); } if (preg_match('/([a-z0-9_]+\\.[a-z0-9_]+)(?:\\[(.+?)\\](.*))?/i', $paramKey, $matches)) { switch ($matches[1]) { case 'request.queryString': $row->longDescription = "Blocked for " . $row->actionDescription . ' in query string: ' . $matches[2] . '=' . $paramValue; break; case 'request.body': $row->longDescription = "Blocked for " . $row->actionDescription . ' in POST body: ' . $matches[2] . '=' . $paramValue; break; case 'request.cookie': $row->longDescription = "Blocked for " . $row->actionDescription . ' in cookie: ' . $matches[2] . '=' . $paramValue; break; case 'request.fileNames': $row->longDescription = "Blocked for a " . $row->actionDescription . ' in file: ' . $matches[2] . '=' . $paramValue; break; } } $date = date_i18n('F j, Y g:ia', floor($row->attackLogTime)); $dateMax = max(strlen($date), $dateMax); $ip = wfUtils::inet_ntop($row->IP); $ipMax = max(strlen($ip), $ipMax); $country = wfUtils::countryCode2Name(wfUtils::IP2Country($ip)); $country = empty($country) ? 'Unknown' : $country; $countryMax = max(strlen($country), $countryMax); $attackTable[] = array('date' => $date, 'IP' => $ip, 'country' => $country, 'message' => $row->longDescription); } foreach ($attackTable as $row) { $date = str_pad($row['date'], $dateMax + 2); $ip = str_pad($row['IP'] . " ({$row['country']})", $ipMax + $countryMax + 8); $attackMessage = $row['message']; $message .= $date . $ip . $attackMessage . "\n"; } self::alert('Increased Attack Rate', $message, false); wfConfig::set('wafAlertLastSendTime', time()); } } //Send attack data $limit = 500; $lastSendTime = wfConfig::get('lastAttackDataSendTime'); $attackData = $wpdb->get_results($wpdb->prepare("SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->base_prefix}wfHits\nWHERE action in ('blocked:waf', 'learned:waf', 'logged:waf', 'blocked:waf-always')\nAND attackLogTime > %.6f\nLIMIT %d", $lastSendTime, $limit)); $totalRows = $wpdb->get_var('SELECT FOUND_ROWS()'); if ($attackData && wfConfig::get('other_WFNet', true)) { $response = wp_remote_get(sprintf(WFWAF_API_URL_SEC . "waf-rules/%d.txt", $waf->getStorageEngine()->getConfig('attackDataKey'))); if (!is_wp_error($response)) { $okToSendBody = wp_remote_retrieve_body($response); if ($okToSendBody === 'ok') { // Build JSON to send $dataToSend = array(); $attackDataToUpdate = array(); foreach ($attackData as $attackDataRow) { $actionData = (array) wfRequestModel::unserializeActionData($attackDataRow->actionData); $dataToSend[] = array($attackDataRow->attackLogTime, $attackDataRow->ctime, wfUtils::inet_ntop($attackDataRow->IP), array_key_exists('learningMode', $actionData) ? $actionData['learningMode'] : 0, array_key_exists('paramKey', $actionData) ? base64_encode($actionData['paramKey']) : false, array_key_exists('paramValue', $actionData) ? base64_encode($actionData['paramValue']) : false, array_key_exists('failedRules', $actionData) ? $actionData['failedRules'] : '', strpos($attackDataRow->URL, 'https') === 0 ? 1 : 0, array_key_exists('fullRequest', $actionData) ? $actionData['fullRequest'] : ''); if (array_key_exists('fullRequest', $actionData)) { unset($actionData['fullRequest']); $attackDataToUpdate[$attackDataRow->id] = array('actionData' => wfRequestModel::serializeActionData($actionData)); } if ($attackDataRow->attackLogTime > $lastSendTime) { $lastSendTime = $attackDataRow->attackLogTime; } } $response = wp_remote_post(WFWAF_API_URL_SEC . "?" . http_build_query(array('action' => 'send_waf_attack_data', 'k' => $waf->getStorageEngine()->getConfig('apiKey'), 's' => $waf->getStorageEngine()->getConfig('siteURL') ? $waf->getStorageEngine()->getConfig('siteURL') : sprintf('%s://%s/', $waf->getRequest()->getProtocol(), rawurlencode($waf->getRequest()->getHost())), 't' => microtime(true)), null, '&'), array('body' => json_encode($dataToSend), 'headers' => array('Content-Type' => 'application/json'), 'timeout' => 30)); if (!is_wp_error($response) && ($body = wp_remote_retrieve_body($response))) { $jsonData = json_decode($body, true); if (is_array($jsonData) && array_key_exists('success', $jsonData)) { // Successfully sent data, remove the full request from the table to reduce storage size foreach ($attackDataToUpdate as $hitID => $dataToUpdate) { $wpdb->update($wpdb->base_prefix . 'wfHits', $dataToUpdate, array('id' => $hitID)); } wfConfig::set('lastAttackDataSendTime', $lastSendTime); if ($totalRows > $limit) { self::scheduleSendAttackData(); } if (array_key_exists('data', $jsonData) && array_key_exists('watchedIPList', $jsonData['data'])) { $waf->getStorageEngine()->setConfig('watchedIPs', $jsonData['data']['watchedIPList']); } } } } else { if (is_string($okToSendBody) && preg_match('/next check in: ([0-9]+)/', $okToSendBody, $matches)) { self::scheduleSendAttackData(time() + $matches[1]); } } // Could be that the server is down, so hold off on sending data for a little while. } else { self::scheduleSendAttackData(time() + 7200); } } else { if (!wfConfig::get('other_WFNet', true)) { wfConfig::set('lastAttackDataSendTime', time()); } } self::trimWfHits(); }
public static function runInstall() { if (self::$runInstallCalled) { return; } self::$runInstallCalled = true; if (function_exists('ignore_user_abort')) { ignore_user_abort(true); } $previous_version = get_option('wordfence_version', '0.0.0'); update_option('wordfence_version', WORDFENCE_VERSION); //In case we have a fatal error we don't want to keep running install. //EVERYTHING HERE MUST BE IDEMPOTENT //Remove old legacy cron job if exists wp_clear_scheduled_hook('wordfence_scheduled_scan'); $schema = new wfSchema(); $schema->createAll(); //if not exists wfConfig::setDefaults(); //If not set $restOfSite = wfConfig::get('cbl_restOfSiteBlocked', 'notset'); if ($restOfSite == 'notset') { wfConfig::set('cbl_restOfSiteBlocked', '1'); } //Install new schedule. If schedule config is blank it will install the default 'auto' schedule. wordfence::scheduleScans(); if (wfConfig::get('autoUpdate') == '1') { wfConfig::enableAutoUpdate(); //Sets up the cron } if (!wfConfig::get('apiKey')) { $api = new wfAPI('', wfUtils::getWPVersion()); try { $keyData = $api->call('get_anon_api_key'); if ($keyData['ok'] && $keyData['apiKey']) { wfConfig::set('apiKey', $keyData['apiKey']); } else { throw new Exception("Could not understand the response we received from the Wordfence servers when applying for a free API key."); } } catch (Exception $e) { error_log("Could not fetch free API key from Wordfence: " . $e->getMessage()); return; } } wp_clear_scheduled_hook('wordfence_daily_cron'); wp_clear_scheduled_hook('wordfence_hourly_cron'); if (is_main_site()) { wp_schedule_event(time(), 'daily', 'wordfence_daily_cron'); //'daily' wp_schedule_event(time(), 'hourly', 'wordfence_hourly_cron'); } $db = new wfDB(); if ($db->columnExists('wfHits', 'HTTPHeaders')) { //Upgrade from 3.0.4 global $wpdb; $prefix = $wpdb->base_prefix; $count = $db->querySingle("select count(*) as cnt from {$prefix}" . "wfHits"); if ($count > 20000) { $db->queryWrite("delete from {$prefix}" . "wfHits order by id asc limit " . ($count - 20000)); } $db->dropColumn('wfHits', 'HTTPHeaders'); } //Upgrading from 1.5.6 or earlier needs: $db->createKeyIfNotExists('wfStatus', 'level', 'k2'); if (wfConfig::get('isPaid') == 'free') { wfConfig::set('isPaid', ''); } //End upgrade from 1.5.6 /** @var wpdb $wpdb */ global $wpdb; $prefix = $wpdb->base_prefix; $db->queryWriteIgnoreError("alter table {$prefix}" . "wfConfig modify column val longblob"); $db->queryWriteIgnoreError("alter table {$prefix}" . "wfBlocks add column permanent tinyint UNSIGNED default 0"); $db->queryWriteIgnoreError("alter table {$prefix}" . "wfStatus modify column msg varchar(1000) NOT NULL"); //3.1.2 to 3.1.4 $db->queryWriteIgnoreError("alter table {$prefix}" . "wfBlocks modify column blockedTime bigint signed NOT NULL"); //3.2.1 to 3.2.2 $db->queryWriteIgnoreError("alter table {$prefix}" . "wfLockedOut modify column blockedTime bigint signed NOT NULL"); $db->queryWriteIgnoreError("drop table if exists {$prefix}" . "wfFileQueue"); $db->queryWriteIgnoreError("drop table if exists {$prefix}" . "wfFileChanges"); $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}wfStatus where field = 'id'"); if (!$result || strtolower($result->Key) != 'pri') { //Adding primary key to this table because some backup apps use primary key during backup. $db->queryWriteIgnoreError("alter table {$prefix}wfStatus add id bigint UNSIGNED NOT NULL auto_increment PRIMARY KEY"); } $optScanEnabled = $db->querySingle("select val from {$prefix}" . "wfConfig where name='scansEnabled_options'"); if ($optScanEnabled != '0' && $optScanEnabled != '1') { $db->queryWrite("update {$prefix}" . "wfConfig set val='1' where name='scansEnabled_options'"); } $optScanEnabled = $db->querySingle("select val from {$prefix}" . "wfConfig where name='scansEnabled_heartbleed'"); if ($optScanEnabled != '0' && $optScanEnabled != '1') { //Enable heartbleed if no value is set. wfConfig::set('scansEnabled_heartbleed', 1); } if (wfConfig::get('cacheType') == 'php' || wfConfig::get('cacheType') == 'falcon') { wfCache::removeCacheDirectoryHtaccess(); } // IPv6 schema changes for 6.0.1 $tables_with_ips = array('wfCrawlers', 'wfBadLeechers', 'wfBlockedIPLog', 'wfBlocks', 'wfHits', 'wfLeechers', 'wfLockedOut', 'wfLocs', 'wfLogins', 'wfReverseCache', 'wfScanners', 'wfThrottleLog', 'wfVulnScanners'); foreach ($tables_with_ips as $ip_table) { $result = $wpdb->get_row("SHOW FIELDS FROM {$prefix}{$ip_table} where field = 'IP'"); if (!$result || strtolower($result->Type) == 'binary(16)') { continue; } $db->queryWriteIgnoreError("ALTER TABLE {$prefix}{$ip_table} 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 {$prefix}{$ip_table} where field = 'IP'"); if (!$result || strtolower($result->Type) != 'binary(16)') { continue; } $db->queryWriteIgnoreError("UPDATE {$prefix}{$ip_table} SET IP = CONCAT(LPAD(CHAR(0xff, 0xff), 12, CHAR(0)), LPAD(\n\tCHAR(\n\t\tCAST(IP as UNSIGNED) >> 24 & 0xFF,\n\t\tCAST(IP as UNSIGNED) >> 16 & 0xFF,\n\t\tCAST(IP as UNSIGNED) >> 8 & 0xFF,\n\t\tCAST(IP as UNSIGNED) & 0xFF\n\t),\n\t4,\n\tCHAR(0)\n))"); } // Fix the data in the country column. // TODO: add version check so this doesn't run on every update. $ip_results = $wpdb->get_results("SELECT * FROM `{$prefix}wfBlockedIPLog` GROUP BY IP"); if ($ip_results) { foreach ($ip_results as $ip_row) { $wpdb->query($wpdb->prepare("UPDATE `{$prefix}wfBlockedIPLog` SET countryCode = %s WHERE IP = %s", wfUtils::IP2Country(wfUtils::inet_ntop($ip_row->IP)), $ip_row->IP)); } } //Must be the final line }
} } ?> </tr> </table> </div> <table border="0" cellpadding="0" cellspacing="0"><tr> <td><input type="button" name="but4" class="button-primary" value="Save blocking options and country list" onclick="WFAD.saveCountryBlocking();" /></td> <td style="height: 24px;"><div class="wfAjax24"></div><span class="wfSavedMsg"> Your changes have been saved!</span></td></tr> </table> <span style="font-size: 10px;">Note that we use an IP to country database that is 99.5% accurate to identify which country a visitor is from.</span> </div> </div> <script type="text/javascript"> jQuery(function(){ WFAD.setOwnCountry('<?php echo wfUtils::IP2Country(wfUtils::getIP()); ?> '); }); <?php if (wfConfig::get('cbl_countries')) { ?> jQuery(function(){ WFAD.loadBlockedCountries('<?php echo wfConfig::get('cbl_countries'); ?> '); }); <?php } ?> </script> <script type="text/x-jquery-template" id="wfWelcomeContentCntBlk"> <div>
public function checkForBlockedCountry() { static $hasRun; if (isset($hasRun)) { return; } $hasRun = true; $blockedCountries = wfConfig::get('cbl_countries', false); $bareRequestURI = untrailingslashit(wfUtils::extractBareURI($_SERVER['REQUEST_URI'])); $IP = wfUtils::getIP(); if ($country = wfUtils::IP2Country($IP)) { foreach (explode(',', $blockedCountries) as $blocked) { if (strtoupper($blocked) == strtoupper($country)) { //At this point we know the user has been blocked if (wfConfig::get('cbl_action') == 'redir') { $redirURL = wfConfig::get('cbl_redirURL'); $eRedirHost = wfUtils::extractHostname($redirURL); $isExternalRedir = false; if ($eRedirHost && $eRedirHost != wfUtils::extractHostname(home_url())) { //It's an external redirect... $isExternalRedir = true; } if (!$isExternalRedir && untrailingslashit(wfUtils::extractBareURI($redirURL)) == $bareRequestURI) { //Is this the URI we want to redirect to, then don't block it //Do nothing /* Uncomment the following if page components aren't loading for the page we redirect to. Uncommenting is not recommended because it means that anyone from a blocked country can crawl your site by sending the page blocked users are redirected to as the referer for every request. But it's your call. } else if(wfUtils::extractBareURI($_SERVER['HTTP_REFERER']) == $redirURL){ //If the referer the page we want to redirect to? Then this might be loading as a component so don't block. //Do nothing */ } else { wfConfig::inc('totalCountryBlocked'); $this->initLogRequest(); $this->currentRequest->actionDescription = 'blocked access via country blocking and redirected to URL (' . wfConfig::get('cbl_redirURL') . ')'; $this->currentRequest->statusCode = 503; if (!$this->currentRequest->action) { $this->currentRequest->action = 'blocked:wordfence'; } $this->logHit(); wfActivityReport::logBlockedIP($IP); $this->redirect(wfConfig::get('cbl_redirURL')); } } else { $this->currentRequest->actionDescription = 'blocked access via country blocking'; wfConfig::inc('totalCountryBlocked'); wfActivityReport::logBlockedIP($IP); $this->do503(3600, "Access from your area has been temporarily limited for security reasons"); } } } } }
echo "</tr><tr>\n"; } } ?> </tr> </table> </div> <table border="0" cellpadding="0" cellspacing="0"><tr> <td><input type="button" name="but4" class="button-primary" value="Save blocking options and country list" onclick="WFAD.saveCountryBlocking();" /></td> <td style="height: 24px;"><div class="wfAjax24"></div><span class="wfSavedMsg"> Your changes have been saved!</span></td></tr> </table> <span style="font-size: 10px;">Note that we use an IP to country database that is 99.5% accurate to identify which country a visitor is from.</span> </div> </div> <script type="text/javascript"> jQuery(function(){ WFAD.setOwnCountry('<?php echo wfUtils::IP2Country(wfUtils::getIP()); ?>'); }); <?php if(wfConfig::get('cbl_countries')){ ?> jQuery(function(){ WFAD.loadBlockedCountries('<?php echo wfConfig::get('cbl_countries'); ?>'); }); <?php } ?> </script> <script type="text/x-jquery-template" id="wfWelcomeContentCntBlk"> <div> <h3>Premium Feature: Block or redirect countries</h3> <strong><p>Being targeted by hackers in a specific country?</p></strong> <p> The premium version of Wordfence offers country blocking. This uses a commercial geolocation database to block hackers, spammers