?> "/> 0 or empty means unlimited issues will be sent. </td> </tr> <tr> <th>Time limit that a scan can run in seconds.<a href="http://docs.wordfence.com/en/Wordfence_options#Time_limit_that_a_scan_can_run_in_seconds" target="_blank" class="wfhelp"></a></th></th> <td> <input type="text" name="scan_maxDuration" id="scan_maxDuration" value="<?php $w->f('scan_maxDuration'); ?> "/> 0 or empty means the default of <?php echo wfUtils::makeDuration(WORDFENCE_DEFAULT_MAX_SCAN_TIME); ?> will be used. </td> </tr> <tr> <td colspan="2"> <div class="wfMarker" id="wfMarkerFirewallRules"></div> <h3 class="wfConfigHeading">Rate Limiting Rules<a href="http://docs.wordfence.com/en/Wordfence_options#Rate_Limiting_Rules" target="_blank" class="wfhelp"></a></h3> </td> </tr> <tr> <th>Immediately block fake Google crawlers:<a href="http://docs.wordfence.com/en/Wordfence_options#Immediately_block_fake_Google_crawlers:"
/** * */ 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(); }
private function doScan() { if (wfConfig::get('lowResourceScansEnabled')) { $isFork = $_GET['isFork'] == '1' ? true : false; wfConfig::set('lowResourceScanWaitStep', !wfConfig::get('lowResourceScanWaitStep')); if ($isFork && wfConfig::get('lowResourceScanWaitStep')) { sleep($this->maxExecTime / 2); $this->fork(); //exits } } while (sizeof($this->jobList) > 0) { self::checkForKill(); $jobName = $this->jobList[0]; $callback = array($this, 'scan_' . $jobName); if (is_callable($callback)) { call_user_func($callback); } array_shift($this->jobList); //only shift once we're done because we may pause halfway through a job and need to pick up where we left off self::checkForKill(); if ($this->forkRequested) { $this->fork(); } else { $this->forkIfNeeded(); } } $summary = $this->i->getSummaryItems(); $this->status(1, 'info', '-------------------'); $this->status(1, 'info', "Scan Complete. Scanned " . $summary['totalFiles'] . " files, " . $summary['totalPlugins'] . " plugins, " . $summary['totalThemes'] . " themes, " . ($summary['totalPages'] + $summary['totalPosts']) . " pages, " . $summary['totalComments'] . " comments and " . $summary['totalRows'] . " records in " . wfUtils::makeDuration(time() - $this->startTime, true) . "."); if ($this->i->totalIssues > 0) { $this->status(10, 'info', "SUM_FINAL:Scan complete. You have " . $this->i->totalIssues . " new issues to fix. See below."); } else { $this->status(10, 'info', "SUM_FINAL:Scan complete. Congratulations, no problems found."); } return; }