Server IP : 192.64.118.117 / Your IP : 18.119.108.165 Web Server : LiteSpeed System : Linux premium56.web-hosting.com 4.18.0-513.24.1.lve.1.el8.x86_64 #1 SMP Thu May 9 15:10:09 UTC 2024 x86_64 User : thecgapy ( 1160) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/thecgapy/mobilecreationz.com/wp-content/plugins/wordfence/lib/ |
Upload File : |
<?php require_once('wfDB.php'); require_once('wfUtils.php'); require_once('wfBrowscap.php'); class wfLog { public $canLogHit = true; private $hitsTable = ''; private $apiKey = ''; private $wp_version = ''; private $db = false; private $googlePattern = '/\.(?:googlebot\.com|google\.[a-z]{2,3}|google\.[a-z]{2}\.[a-z]{2}|1e100\.net)$/i'; private static $gbSafeCache = array(); /** * @var wfRequestModel */ private $currentRequest; public function __construct($apiKey, $wp_version){ $this->apiKey = $apiKey; $this->wp_version = $wp_version; global $wpdb; $this->hitsTable = $wpdb->base_prefix . 'wfHits'; $this->loginsTable = $wpdb->base_prefix . 'wfLogins'; $this->blocksTable = $wpdb->base_prefix . 'wfBlocks'; $this->lockOutTable = $wpdb->base_prefix . 'wfLockedOut'; $this->leechTable = $wpdb->base_prefix . 'wfLeechers'; $this->badLeechersTable = $wpdb->base_prefix . 'wfBadLeechers'; $this->scanTable = $wpdb->base_prefix . 'wfScanners'; $this->throttleTable = $wpdb->base_prefix . 'wfThrottleLog'; $this->statusTable = $wpdb->base_prefix . 'wfStatus'; $this->ipRangesTable = $wpdb->base_prefix . 'wfBlocksAdv'; $this->perfTable = $wpdb->base_prefix . 'wfPerfLog'; } public function initLogRequest() { if ($this->currentRequest === null) { $this->currentRequest = new wfRequestModel(); $this->currentRequest->ctime = sprintf('%.6f', microtime(true)); $this->currentRequest->statusCode = 200; $this->currentRequest->isGoogle = (wfCrawl::isGoogleCrawler() ? 1 : 0); $this->currentRequest->IP = wfUtils::inet_pton(wfUtils::getIP()); $this->currentRequest->userID = $this->getCurrentUserID(); $this->currentRequest->newVisit = (wordfence::$newVisit ? 1 : 0); $this->currentRequest->URL = wfUtils::getRequestedURL(); $this->currentRequest->referer = (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''); $this->currentRequest->UA = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''); $this->currentRequest->jsRun = 0; if (!function_exists('wp_verify_nonce')) { add_action('plugins_loaded', array($this, 'actionSetRequestJSEnabled')); } else { $this->actionSetRequestJSEnabled(); } add_action('init', array($this, 'actionSetRequestOnInit'), 9999); if (function_exists('register_shutdown_function')) { register_shutdown_function(array($this, 'logHit')); } } } public function actionSetRequestJSEnabled() { $UA = $this->currentRequest->UA; $IP = wfUtils::getIP(); $jsRun = (int) (isset($_COOKIE['wordfence_verifiedHuman']) && $this->validateVerifiedHumanCookie($_COOKIE['wordfence_verifiedHuman'], $UA, $IP)); $this->currentRequest->jsRun = $jsRun; } /** * CloudFlare's plugin changes $_SERVER['REMOTE_ADDR'] on init. */ public function actionSetRequestOnInit() { $this->currentRequest->IP = wfUtils::inet_pton(wfUtils::getIP()); $this->currentRequest->userID = $this->getCurrentUserID(); } /** * @param string $cookieVal * @param string $ua * @param string $ip * @return string */ public function validateVerifiedHumanCookie($cookieVal, $ua = null, $ip = null) { if ($ua === null) { $ua = !empty($this->currentRequest) ? $this->currentRequest->UA : ''; } if ($ip === null) { $ip = wfUtils::getIP(); } if (!function_exists('hash_equals')) { require_once ABSPATH . WPINC . '/compat.php'; } return hash_equals($cookieVal, $this->getVerifiedHumanCookieValue($ua, $ip)); } /** * @param string $ua * @param string $ip * @return string */ public function getVerifiedHumanCookieValue($ua = null, $ip = null) { if ($ua === null) { $ua = !empty($this->currentRequest) ? $this->currentRequest->UA : ''; } if ($ip === null) { $ip = wfUtils::getIP(); } if (!function_exists('wp_hash')) { require_once ABSPATH . WPINC . '/pluggable.php'; } return wp_hash('wordfence_verifiedHuman' . $ua . $ip, 'nonce'); } /** * @return wfRequestModel */ public function getCurrentRequest() { return $this->currentRequest; } public function logPerf($IP, $UA, $URL, $data){ $IP = wfUtils::inet_pton($IP); $this->getDB()->queryWrite("insert into " . $this->perfTable . " (IP, userID, UA, URL, ctime, fetchStart, domainLookupStart, domainLookupEnd, connectStart, connectEnd, requestStart, responseStart, responseEnd, domReady, loaded) values (%s, %d, '%s', '%s', unix_timestamp(), %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", $IP, $this->getCurrentUserID(), $UA, $URL, $data['fetchStart'], $data['domainLookupStart'], $data['domainLookupEnd'], $data['connectStart'], $data['connectEnd'], $data['requestStart'], $data['responseStart'], $data['responseEnd'], $data['domReady'], $data['loaded'] ); } public function logLogin($action, $fail, $username){ if(! $username){ return; } $user = get_user_by('login', $username); $userID = 0; if($user){ $userID = $user->ID; if(! $userID){ return; } } else { $user = get_user_by('email', $username); if ($user) { $userID = $user->ID; if (!$userID) { return; } } } // change the action flag here if the user does not exist. if ($action == 'loginFailValidUsername' && $userID == 0) { $action = 'loginFailInvalidUsername'; } $hitID = 0; if ($this->currentRequest !== null) { $this->currentRequest->userID = $userID; $this->currentRequest->action = $action; $this->currentRequest->save(); $hitID = $this->currentRequest->getPrimaryKey(); } //Else userID stays 0 but we do log this even though the user doesn't exist. $this->getDB()->queryWrite("insert into " . $this->loginsTable . " (hitID, ctime, fail, action, username, userID, IP, UA) values (%d, %f, %d, '%s', '%s', %s, %s, '%s')", $hitID, sprintf('%.6f', microtime(true)), $fail, $action, $username, $userID, wfUtils::inet_pton(wfUtils::getIP()), (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '') ); } private function getCurrentUserID(){ if (!function_exists('get_current_user_id') || !defined('AUTH_COOKIE')) { //If pluggable.php is loaded early by some other plugin on a multisite installation, it leads to an error because AUTH_COOKIE is undefined and WP doesn't check for it first return 0; } $id = get_current_user_id(); return $id ? $id : 0; } public function logLeechAndBlock($type){ //404 or hit if(wfConfig::get('firewallEnabled')){ //Moved the following block into the "is fw enabled section" for optimization. $IP = wfUtils::getIP(); $IPnum = wfUtils::inet_pton($IP); if($this->isWhitelisted($IP)){ return; } if (wfConfig::get('neverBlockBG') == 'neverBlockUA' && wfCrawl::isGoogleCrawler()) { return; } if (wfConfig::get('neverBlockBG') == 'neverBlockVerified' && wfCrawl::isVerifiedGoogleCrawler()) { return; } if ($type == '404') { $allowed404s = wfConfig::get('allowed404s'); if (is_string($allowed404s)) { $allowed404s = array_filter(preg_split("/[\r\n]+/", $allowed404s)); $allowed404sPattern = ''; foreach ($allowed404s as $allowed404) { $allowed404sPattern .= preg_replace('/\\\\\*/', '.*?', preg_quote($allowed404, '/')) . '|'; } $uri = $_SERVER['REQUEST_URI']; if (($index = strpos($uri, '?')) !== false) { $uri = substr($uri, 0, $index); } if ($allowed404sPattern && preg_match('/^' . substr($allowed404sPattern, 0, -1) . '$/i', $uri)) { return; } } } if($type == '404'){ $table = $this->scanTable; } else if($type == 'hit'){ $table = $this->leechTable; } else { wordfence::status(1, 'error', "Invalid type to logLeechAndBlock(): $type"); return; } $this->getDB()->queryWrite("insert into $table (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfcurrenthits := hits + 1, hits + 1, hits + 1)", wfUtils::inet_pton($IP)); $hitsPerMinute = $this->getDB()->querySingle("select @wfcurrenthits"); //end block moved into "is fw enabled" section //Range blocking was here. Moved to wordfenceClass::veryFirstAction if(wfConfig::get('maxGlobalRequests') != 'DISABLED' && $hitsPerMinute > wfConfig::get('maxGlobalRequests')){ //Applies to 404 or pageview $this->takeBlockingAction('maxGlobalRequests', "Exceeded the maximum global requests per minute for crawlers or humans."); } if($type == '404'){ global $wpdb; $p = $wpdb->base_prefix; if(wfConfig::get('other_WFNet')){ $this->getDB()->queryWrite("insert IGNORE into $p"."wfNet404s (sig, ctime, URI) values (UNHEX(MD5('%s')), unix_timestamp(), '%s')", $_SERVER['REQUEST_URI'], $_SERVER['REQUEST_URI']); } $pat = wfConfig::get('vulnRegex'); if($pat){ $URL = wfUtils::getRequestedURL(); if(preg_match($pat, $URL)){ $this->getDB()->queryWrite("insert IGNORE into $p"."wfVulnScanners (IP, ctime, hits) values (%s, unix_timestamp(), 1) ON DUPLICATE KEY UPDATE ctime = unix_timestamp(), hits = hits + 1", wfUtils::inet_pton($IP)); if(wfConfig::get('maxScanHits') != 'DISABLED'){ if( empty($_SERVER['HTTP_REFERER'] )){ $this->getDB()->queryWrite("insert into " . $this->badLeechersTable . " (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfblcurrenthits := hits + 1, hits + 1, hits + 1)", $IPnum); $BL_hitsPerMinute = $this->getDB()->querySingle("select @wfblcurrenthits"); if($BL_hitsPerMinute > wfConfig::get('maxScanHits')){ $this->takeBlockingAction('maxScanHits', "Exceeded the maximum number of 404 requests per minute for a known security vulnerability."); } } } } } } if(isset($_SERVER['HTTP_USER_AGENT']) && wfCrawl::isCrawler($_SERVER['HTTP_USER_AGENT'])){ if($type == 'hit' && wfConfig::get('maxRequestsCrawlers') != 'DISABLED' && $hitsPerMinute > wfConfig::get('maxRequestsCrawlers')){ $this->takeBlockingAction('maxRequestsCrawlers', "Exceeded the maximum number of requests per minute for crawlers."); //may not exit } else if($type == '404' && wfConfig::get('max404Crawlers') != 'DISABLED' && $hitsPerMinute > wfConfig::get('max404Crawlers')){ $this->takeBlockingAction('max404Crawlers', "Exceeded the maximum number of page not found errors per minute for a crawler."); } } else { if($type == 'hit' && wfConfig::get('maxRequestsHumans') != 'DISABLED' && $hitsPerMinute > wfConfig::get('maxRequestsHumans')){ $this->takeBlockingAction('maxRequestsHumans', "Exceeded the maximum number of page requests per minute for humans."); } else if($type == '404' && wfConfig::get('max404Humans') != 'DISABLED' && $hitsPerMinute > wfConfig::get('max404Humans')){ $this->takeBlockingAction('max404Humans', "Exceeded the maximum number of page not found errors per minute for humans."); } } } } /** * @param string $IP Should be in dot or colon notation (127.0.0.1 or ::1) * @param bool $forcedWhitelistEntry If provided, returns whether or not the IP is on a forced whitelist. * @return bool */ public function isWhitelisted($IP, &$forcedWhitelistEntry = null) { if ($forcedWhitelistEntry !== null) { $forcedWhitelistEntry = false; } foreach (wfUtils::getIPWhitelist() as $subnet) { if ($subnet instanceof wfUserIPRange) { if ($subnet->isIPInRange($IP)) { return true; } } elseif (wfUtils::subnetContainsIP($subnet, $IP)) { if ($forcedWhitelistEntry !== null) { $forcedWhitelistEntry = true; } return true; } } return false; } public function unblockAllIPs(){ $this->getDB()->queryWrite("delete from " . $this->blocksTable); if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::synchronizeConfigSettings(); } } public function unlockAllIPs(){ $this->getDB()->queryWrite("delete from " . $this->lockOutTable); if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::synchronizeConfigSettings(); } } public function unblockIP($IP){ $this->getDB()->queryWrite("delete from " . $this->blocksTable . " where IP=%s", wfUtils::inet_pton($IP)); if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::synchronizeConfigSettings(); } } public function unblockRange($id){ $this->getDB()->queryWrite("delete from " . $this->ipRangesTable . " where id=%d", $id); if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::synchronizeConfigSettings(); } } /** * * @param string $blockType * @param string $range * @param string $reason * @return bool */ public function blockRange($blockType, $range, $reason){ $reason = stripslashes($reason); $this->getDB()->queryWrite("insert IGNORE into " . $this->ipRangesTable . " (blockType, blockString, ctime, reason, totalBlocked, lastBlocked) values ('%s', '%s', unix_timestamp(), '%s', 0, 0)", $blockType, $range, $reason); if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::synchronizeConfigSettings(); } return true; } public function getRangesBasic(){ $results = $this->getDB()->querySelect("select blockString from " . $this->ipRangesTable); if(is_array($results) && sizeof($results) > 0){ $ret = array(); foreach($results as $r){ $ret[] = $r['blockString']; } return $ret; } else { return false; } } public function getRanges(){ $results = $this->getDB()->querySelect("select id, blockType, blockString, unix_timestamp() - ctime as ctimeAgo, reason, totalBlocked, unix_timestamp() - lastBlocked as lastBlockedAgo, lastBlocked from " . $this->ipRangesTable . " order by ctime desc"); foreach($results as &$elem){ if($elem['blockType'] != 'IU'){ continue; } //We only use IU type for now, but have this for future different block types. $elem['ctimeAgo'] = wfUtils::makeTimeAgo($elem['ctimeAgo']); if($elem['lastBlocked'] > 0){ $elem['lastBlockedAgo'] = wfUtils::makeTimeAgo($elem['lastBlockedAgo']) . ' ago'; } else { $elem['lastBlockedAgo'] = 'Never'; } $blockDat = explode('|', $elem['blockString']); $elem['ipPattern'] = ""; $numBlockElements = 0; if($blockDat[0]){ $numBlockElements++; list($start_range, $end_range) = explode('-', $blockDat[0]); if (!preg_match('/[\.:]/', $start_range)) { $start_range = long2ip($start_range); $end_range = long2ip($end_range); } $elem['ipPattern'] = "Block visitors with IP addresses in the range: " . $start_range . ' - ' . $end_range; } else { $elem['ipPattern'] = 'Allow all IP addresses'; } if($blockDat[1]){ $numBlockElements++; $elem['browserPattern'] = "Block visitors whos browsers match the pattern: " . $blockDat[1]; } else { $elem['browserPattern'] = 'Allow all browsers'; } if($blockDat[2]){ $numBlockElements++; $elem['refererPattern'] = "Block visitors from websites that match the pattern: " . $blockDat[2]; } else { $elem['refererPattern'] = "Allow visitors arriving from all websites"; } if (! empty($blockDat[3])) { $elem['hostnamePattern'] = $blockDat[3]; } $elem['patternDisabled'] = false; } return $results; } public function blockIP($IP, $reason, $wfsn = false, $permanent = false, $maxTimeBlocked = false){ //wfsn indicates it comes from Wordfence secure network if($this->isWhitelisted($IP)){ return false; } $wfsn = $wfsn ? 1 : 0; $timeBlockOccurred = $this->getDB()->querySingle("select unix_timestamp() as ctime"); $durationOfBlocks = wfConfig::get('blockedTime'); if($maxTimeBlocked && $durationOfBlocks > $maxTimeBlocked){ $timeBlockOccurred -= ($durationOfBlocks - $maxTimeBlocked); } if($permanent){ //Insert permanent=1 or update existing perm or non-per block to be permanent $this->getDB()->queryWrite("insert into " . $this->blocksTable . " (IP, blockedTime, reason, wfsn, permanent) values (%s, %d, '%s', %d, %d) ON DUPLICATE KEY update blockedTime=%d, reason='%s', wfsn=%d, permanent=%d", wfUtils::inet_pton($IP), $timeBlockOccurred, $reason, $wfsn, 1, $timeBlockOccurred, $reason, $wfsn, 1 ); } else { //insert perm=0 but don't update and make perm blocks non-perm. $this->getDB()->queryWrite("insert into " . $this->blocksTable . " (IP, blockedTime, reason, wfsn, permanent) values (%s, %d, '%s', %d, %d) ON DUPLICATE KEY update blockedTime=%d, reason='%s', wfsn=%d", wfUtils::inet_pton($IP), $timeBlockOccurred, $reason, $wfsn, 0, $timeBlockOccurred, $reason, $wfsn ); } wfActivityReport::logBlockedIP($IP); if ($this->currentRequest !== null) { $this->currentRequest->statusCode = 403; $this->currentRequest->action = 'blocked:' . ($wfsn ? 'wfsn' : 'wordfence'); $this->currentRequest->actionDescription = $reason; } wfConfig::inc('totalIPsBlocked'); if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::synchronizeConfigSettings(); } return true; } public function lockOutIP($IP, $reason){ if($this->isWhitelisted($IP)){ return false; } $reason = stripslashes($reason); $this->getDB()->queryWrite("insert into " . $this->lockOutTable . " (IP, blockedTime, reason) values (%s, unix_timestamp(), '%s') ON DUPLICATE KEY update blockedTime=unix_timestamp(), reason='%s'", wfUtils::inet_pton($IP), $reason, $reason ); wfActivityReport::logBlockedIP($IP); if ($this->currentRequest !== null) { $this->currentRequest->statusCode = 403; $this->currentRequest->action = 'lockedOut'; $this->currentRequest->actionDescription = $reason; } wfConfig::inc('totalIPsLocked'); if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::synchronizeConfigSettings(); } return true; } public function unlockOutIP($IP){ $this->getDB()->queryWrite("delete from " . $this->lockOutTable . " where IP=%s", wfUtils::inet_pton($IP)); if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::synchronizeConfigSettings(); } } public function isIPLockedOut($IP){ if($this->getDB()->querySingle("select IP from " . $this->lockOutTable . " where IP=%s and blockedTime + %s > unix_timestamp()", wfUtils::inet_pton($IP), wfConfig::get('loginSec_lockoutMins') * 60)){ $this->getDB()->queryWrite("update " . $this->lockOutTable . " set blockedHits = blockedHits + 1, lastAttempt = unix_timestamp() where IP=%s", wfUtils::inet_pton($IP)); return true; } else { return false; } } public function getThrottledIPs($offset = 0){ $results = $this->getDB()->querySelect("select IP, startTime, endTime, timesThrottled, lastReason, unix_timestamp() - startTime as startTimeAgo, unix_timestamp() - endTime as endTimeAgo from " . $this->throttleTable . " order by endTime desc limit 50"); foreach($results as &$elem){ $elem['startTimeAgo'] = wfUtils::makeTimeAgo($elem['startTimeAgo']); $elem['endTimeAgo'] = wfUtils::makeTimeAgo($elem['endTimeAgo']); } $this->resolveIPs($results); foreach($results as &$elem){ $elem['IP'] = wfUtils::inet_ntop($elem['IP']); } return $results; } public function getLockedOutIPs($offset = 0, &$total = null){ $lockoutSecs = wfConfig::get('loginSec_lockoutMins') * 60; $results = $this->getDB()->querySelect("select SQL_CALC_FOUND_ROWS IP, unix_timestamp() - blockedTime as createdAgo, reason, unix_timestamp() - lastAttempt as lastAttemptAgo, lastAttempt, blockedHits, (blockedTime + %s) - unix_timestamp() as blockedFor from " . $this->lockOutTable . " where blockedTime + %s > unix_timestamp() order by blockedTime desc, IP desc LIMIT %d, %d", $lockoutSecs, $lockoutSecs, $offset, WORDFENCE_BLOCKED_IPS_PER_PAGE); if ($total !== null) { $total = $this->getDB()->querySingle('SELECT FOUND_ROWS()'); } foreach($results as &$elem){ $elem['lastAttemptAgo'] = $elem['lastAttempt'] ? wfUtils::makeTimeAgo($elem['lastAttemptAgo']) : ''; $elem['blockedForAgo'] = wfUtils::makeTimeAgo($elem['blockedFor']); } $this->resolveIPs($results); foreach($results as &$elem){ $elem['IP'] = wfUtils::inet_ntop($elem['IP']); } return $results; } public function getBlockedIPsAddrOnly(){ $results = $this->getDB()->querySelect("select IP from " . $this->blocksTable . " where (permanent=1 OR (blockedTime + %s > unix_timestamp()))", wfConfig::get('blockedTime'), wfConfig::get('blockedTime')); $ret = array(); foreach($results as $elem){ $ret[] = wfUtils::inet_ntop($elem['IP']); } return $ret; } public function getBlockedIPs($offset = 0, &$total = null){ $results = $this->getDB()->querySelect("select SQL_CALC_FOUND_ROWS IP, unix_timestamp() - blockedTime as createdAgo, reason, unix_timestamp() - lastAttempt as lastAttemptAgo, lastAttempt, blockedHits, (blockedTime + %s) - unix_timestamp() as blockedFor, permanent from " . $this->blocksTable . " where (permanent=1 OR (blockedTime + %s > unix_timestamp())) order by blockedTime desc, IP desc LIMIT %d, %d", wfConfig::get('blockedTime'), wfConfig::get('blockedTime'), $offset, WORDFENCE_BLOCKED_IPS_PER_PAGE); if ($total !== null) { $total = $this->getDB()->querySingle('SELECT FOUND_ROWS()'); } foreach($results as &$elem){ $lastHitAgo = 0; $totalHits = 0; $serverTime = $this->getDB()->querySingle("select unix_timestamp()"); $lastLeech = $this->getDB()->querySingleRec("select max(eMin) * 60 as lastHit, sum(hits) as totalHits from " . $this->leechTable . " where IP=%s", $elem['IP']); //$lastLeech will be true because we use aggregation functions, so check actual values if($lastLeech['lastHit']){ $totalHits += $lastLeech['totalHits']; $lastHitAgo = $serverTime - $lastLeech['lastHit']; $elem['lastHit'] = $lastLeech['lastHit']; } $lastScan = $this->getDB()->querySingleRec("select max(eMin) * 60 as lastHit, sum(hits) as totalHits from " . $this->scanTable . " where IP=%s", $elem['IP']); if($lastScan['lastHit']){ //Checking actual value because we will get a row back from aggregation funcs $totalHits += $lastScan['totalHits']; $lastScanAgo = $serverTime - $lastScan['lastHit']; if($lastScanAgo < $lastHitAgo){ $lastHitAgo = $lastScanAgo; $elem['lastHit'] = $lastScan['lastHit']; } } $elem['totalHits'] = $totalHits; $elem['lastHitAgo'] = $lastHitAgo ? wfUtils::makeTimeAgo($lastHitAgo) : ''; $elem['lastAttemptAgo'] = $elem['lastAttempt'] ? wfUtils::makeTimeAgo($elem['lastAttemptAgo']) : ''; $elem['blockedForAgo'] = wfUtils::makeTimeAgo($elem['blockedFor']); } $this->resolveIPs($results); foreach($results as &$elem){ $elem['blocked'] = 1; $elem['IP'] = wfUtils::inet_ntop($elem['IP']); } return $results; } public function getLeechers($type, $offset = 0){ if($type == 'topScanners'){ $table = $this->scanTable; } else if($type == 'topLeechers'){ $table = $this->leechTable; } else { wordfence::status(1, 'error', "Invalid type to getLeechers(): $type"); return false; } $results = $this->getDB()->querySelect("select IP, sum(hits) as totalHits, eMin * 60 as timestamp, (UNIX_TIMESTAMP() - (eMin * 60)) as timeAgo from $table where eMin > ((unix_timestamp() - 86400) / 60) group by IP order by totalHits desc limit 20"); $this->resolveIPs($results); foreach($results as &$elem){ $elem['timeAgo'] = wfUtils::makeTimeAgo($elem['timeAgo']); $elem['blocked'] = $this->getDB()->querySingle("select blockedTime from " . $this->blocksTable . " where IP=%s and ((blockedTime + %s > unix_timestamp()) OR permanent = 1)", $elem['IP'], wfConfig::get('blockedTime')); //take action $elem['IP'] = wfUtils::inet_ntop($elem['IP']); } return $results; } /** * @return bool|int */ public function logHit(){ $liveTrafficEnabled = wfConfig::liveTrafficEnabled(); $action = $this->currentRequest->action; $logHitOK = $this->logHitOK(); if (!$logHitOK) { return false; } if (!$liveTrafficEnabled && !$action) { return false; } if ($this->currentRequest !== null) { if ($this->currentRequest->save()) { return $this->currentRequest->getPrimaryKey(); } } return false; } public function getPerfStats($afterTime, $limit = 50){ $serverTime = $this->getDB()->querySingle("select unix_timestamp()"); $results = $this->getDB()->querySelect("select * from " . $this->perfTable . " where ctime > %f order by ctime desc limit %d", $afterTime, $limit); $this->resolveIPs($results); $browscap = new wfBrowscap(); foreach($results as &$res){ $res['timeAgo'] = wfUtils::makeTimeAgo($serverTime - $res['ctime']); $res['IP'] = wfUtils::inet_ntop($res['IP']); $res['browser'] = false; if($res['UA']){ $b = $browscap->getBrowser($res['UA']); if ($b && $b['Parent'] != 'DefaultProperties') { $res['browser'] = array( 'browser' => $b['Browser'], 'version' => $b['Version'], 'platform' => $b['Platform'], 'isMobile' => $b['isMobileDevice'], 'isCrawler' => $b['Crawler'] ); } else { $log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion()); $IP = wfUtils::getIP(); $res['browser'] = array( 'isCrawler' => !(isset($_COOKIE['wordfence_verifiedHuman']) && $log->validateVerifiedHumanCookie($_COOKIE['wordfence_verifiedHuman'], $res['UA'], $IP)) ); } } if($res['userID']){ $ud = get_userdata($res['userID']); if($ud){ $res['user'] = array( 'editLink' => wfUtils::editUserLink($res['userID']), 'display_name' => $ud->display_name, 'ID' => $res['userID'] ); $res['user']['avatar'] = get_avatar($res['userID'], 16); } } else { $res['user'] = false; } } return $results; } public function getHits($hitType /* 'hits' or 'logins' */, $type, $afterTime, $limit = 50, $IP = false){ global $wpdb; $IPSQL = ""; if($IP){ $IPSQL = " and IP=%s "; $sqlArgs = array($afterTime, wfUtils::inet_pton($IP), $limit); } else { $sqlArgs = array($afterTime, $limit); } if($hitType == 'hits'){ if($type == 'hit'){ $typeSQL = " "; } else if($type == 'crawler'){ $now = time(); $typeSQL = " and jsRun = 0 and $now - ctime > 30 "; } else if($type == 'gCrawler'){ $typeSQL = " and isGoogle = 1 "; } else if($type == '404'){ $typeSQL = " and statusCode = 404 "; } else if($type == 'human'){ $typeSQL = " and jsRun = 1 "; } else if($type == 'ruser'){ $typeSQL = " and userID > 0 "; } else { wordfence::status(1, 'error', "Invalid log type to wfLog: $type"); return false; } array_unshift($sqlArgs, "select h.*, u.display_name from {$this->hitsTable} h LEFT JOIN {$wpdb->users} u on h.userID = u.ID where ctime > %f $IPSQL $typeSQL order by ctime desc limit %d"); $results = call_user_func_array(array($this->getDB(), 'querySelect'), $sqlArgs); } else if($hitType == 'logins'){ array_unshift($sqlArgs, "select l.*, u.display_name from {$this->loginsTable} l LEFT JOIN {$wpdb->users} u on l.userID = u.ID where ctime > %f $IPSQL order by ctime desc limit %d"); $results = call_user_func_array(array($this->getDB(), 'querySelect'), $sqlArgs ); } else { wordfence::status(1, 'error', "getHits got invalid hitType: $hitType"); return false; } $this->processGetHitsResults($type, $results); return $results; } /** * @param string $type * @param array $results * @throws Exception */ public function processGetHitsResults($type, &$results) { $serverTime = $this->getDB()->querySingle("select unix_timestamp()"); $this->resolveIPs($results); $ourURL = parse_url(site_url()); $ourHost = strtolower($ourURL['host']); $ourHost = preg_replace('/^www\./i', '', $ourHost); $browscap = new wfBrowscap(); $advanced_blocking_results = $this->getDB()->querySelect('SELECT * FROM ' . $this->ipRangesTable); $advanced_blocking = array(); foreach ($advanced_blocking_results as $advanced_blocking_row) { list($blocked_range) = explode('|', $advanced_blocking_row['blockString']); $blocked_range = explode('-', $blocked_range); if (count($blocked_range) == 2) { // Still using v5 32 bit int style format. if (!preg_match('/[\.:]/', $blocked_range[0])) { $blocked_range[0] = long2ip($blocked_range[0]); $blocked_range[1] = long2ip($blocked_range[1]); } $advanced_blocking[] = array(wfUtils::inet_pton($blocked_range[0]), wfUtils::inet_pton($blocked_range[1]), $advanced_blocking_row['id']); } } foreach($results as &$res){ $res['type'] = $type; $res['timeAgo'] = wfUtils::makeTimeAgo($serverTime - $res['ctime']); $res['blocked'] = $this->getDB()->querySingle("select blockedTime from " . $this->blocksTable . " where IP=%s and (permanent = 1 OR (blockedTime + %s > unix_timestamp()))", $res['IP'], wfConfig::get('blockedTime')); $res['rangeBlocked'] = false; $res['ipRangeID'] = -1; foreach ($advanced_blocking as $advanced_blocking_row) { if (strcmp($res['IP'], $advanced_blocking_row[0]) >= 0 && strcmp($res['IP'], $advanced_blocking_row[1]) <= 0) { $res['rangeBlocked'] = true; $res['ipRangeID'] = $advanced_blocking_row[2]; break; } } $res['IP'] = wfUtils::inet_ntop($res['IP']); $res['extReferer'] = false; if(isset( $res['referer'] ) && $res['referer']){ if(wfUtils::hasXSS($res['referer'] )){ //filtering out XSS $res['referer'] = ''; } } if( isset( $res['referer'] ) && $res['referer']){ $refURL = parse_url($res['referer']); if(is_array($refURL) && isset($refURL['host']) && $refURL['host']){ $refHost = strtolower(preg_replace('/^www\./i', '', $refURL['host'])); if($refHost != $ourHost){ $res['extReferer'] = true; //now extract search terms $q = false; if(preg_match('/(?:google|bing|alltheweb|aol|ask)\./i', $refURL['host'])){ $q = 'q'; } else if(stristr($refURL['host'], 'yahoo.')){ $q = 'p'; } else if(stristr($refURL['host'], 'baidu.')){ $q = 'wd'; } if($q){ $queryVars = array(); if( isset( $refURL['query'] ) ) { parse_str($refURL['query'], $queryVars); if(isset($queryVars[$q])){ $res['searchTerms'] = urlencode($queryVars[$q]); } } } } } if($res['extReferer']){ if ( isset( $referringPage ) && stristr( $referringPage['host'], 'google.' ) ) { parse_str( $referringPage['query'], $queryVars ); // echo $queryVars['q']; // This is the search term used } } } $res['browser'] = false; if($res['UA']){ $b = $browscap->getBrowser($res['UA']); if($b && $b['Parent'] != 'DefaultProperties'){ $res['browser'] = array( 'browser' => !empty($b['Browser']) ? $b['Browser'] : "", 'version' => !empty($b['Version']) ? $b['Version'] : "", 'platform' => !empty($b['Platform']) ? $b['Platform'] : "", 'isMobile' => !empty($b['isMobileDevice']) ? $b['isMobileDevice'] : "", 'isCrawler' => !empty($b['Crawler']) ? $b['Crawler'] : "", ); } else { $log = new wfLog(wfConfig::get('apiKey'), wfUtils::getWPVersion()); $IP = wfUtils::getIP(); $res['browser'] = array( 'isCrawler' => !(isset($_COOKIE['wordfence_verifiedHuman']) && $log->validateVerifiedHumanCookie($_COOKIE['wordfence_verifiedHuman'], $res['UA'], $IP)) ? 'true' : '' ); } } if($res['userID']){ $ud = get_userdata($res['userID']); if($ud){ $res['user'] = array( 'editLink' => wfUtils::editUserLink($res['userID']), 'display_name' => $res['display_name'], 'ID' => $res['userID'] ); $res['user']['avatar'] = get_avatar($res['userID'], 16); } } else { $res['user'] = false; } } } public function resolveIPs(&$results){ if(sizeof($results) < 1){ return; } $IPs = array(); foreach($results as &$res){ if($res['IP']){ //Can also be zero in case of non IP events $IPs[] = $res['IP']; } } $IPLocs = wfUtils::getIPsGeo($IPs); //Creates an array with IP as key and data as value foreach($results as &$res){ $ip_printable = wfUtils::inet_ntop($res['IP']); if(isset($IPLocs[$ip_printable])){ $res['loc'] = $IPLocs[$ip_printable]; } else { $res['loc'] = false; } } } public function logHitOK(){ if (!$this->canLogHit) { return false; } if(is_admin()){ return false; } //Don't log admin pageviews if(isset($_SERVER['HTTP_USER_AGENT'])){ if(preg_match('/WordPress\/' . $this->wp_version . '/i', $_SERVER['HTTP_USER_AGENT'])){ return false; } //Ignore requests generated by WP UA. } if($userID = get_current_user_id()){ if(wfConfig::get('liveTraf_ignorePublishers') && (current_user_can('publish_posts') || current_user_can('publish_pages')) ){ return false; } //User is logged in and can publish, so we don't log them. $user = get_userdata($userID); if($user){ if(wfConfig::get('liveTraf_ignoreUsers')){ foreach(explode(',', wfConfig::get('liveTraf_ignoreUsers')) as $ignoreLogin){ if($user->user_login == $ignoreLogin){ return false; } } } } } if(wfConfig::get('liveTraf_ignoreIPs')){ $IPs = explode(',', wfConfig::get('liveTraf_ignoreIPs')); $IP = wfUtils::getIP(); foreach($IPs as $ignoreIP){ if($ignoreIP == $IP){ return false; } } } if( isset($_SERVER['HTTP_USER_AGENT']) && wfConfig::get('liveTraf_ignoreUA') ){ if($_SERVER['HTTP_USER_AGENT'] == wfConfig::get('liveTraf_ignoreUA')){ return false; } } return true; } private function getDB(){ if(! $this->db){ $this->db = new wfDB(); } return $this->db; } public function firewallBadIPs(){ $IP = wfUtils::getIP(); if($this->isWhitelisted($IP)){ return; } $IPnum = wfUtils::inet_pton($IP); $hostname = null; //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 (! empty($bDat[3])) { $ipRange = true; /* We reuse the ipRangeBlocked variable */ if ($hostname === null) { $hostname = wfUtils::reverseLookup($IP); } if (preg_match(wfUtils::patternToRegex($bDat[3]), $hostname)) { $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->currentRequest->actionDescription = 'UA/Referrer/IP Range not allowed'; $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 everything is checked, make sure this always runs. if (wfConfig::get('cbl_loggedInBlocked', false) && wfConfig::get('cbl_loginFormBlocked', false) && wfConfig::get('cbl_restOfSiteBlocked', false)) { $this->checkForBlockedCountry(); } // Block logged in users. if (wfConfig::get('cbl_loggedInBlocked', false) && is_user_logged_in()) { $this->checkForBlockedCountry(); } // Block the login form itself and any attempt to authenticate. if (wfConfig::get('cbl_loginFormBlocked', false)) { if (self::isAuthRequest()) { $this->checkForBlockedCountry(); } add_filter('authenticate', array($this, 'checkForBlockedCountry'), 1, 0); } // Block requests that aren't to the login page, xmlrpc.php, or a user already logged in. if (wfConfig::get('cbl_restOfSiteBlocked', false) && !self::isAuthRequest() && !defined('XMLRPC_REQUEST') && !is_user_logged_in()) { $this->checkForBlockedCountry(); } // XMLRPC is inaccesible when public portion of the site and auth is disabled. if (wfConfig::get('cbl_loginFormBlocked', false) && wfConfig::get('cbl_restOfSiteBlocked', false) && defined('XMLRPC_REQUEST')) { $this->checkForBlockedCountry(); } } } 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') && self::isAuthRequest()){ //It's an auth request and this IP has been blocked $this->getCurrentRequest()->action = 'blocked:wfsnrepeat'; wordfence::wfsnReportBlockedAttempt($IP, 'login'); } $this->do503($secsToGo, $rec['reason']); } } public function getCBLCookieVal(){ $val = wfConfig::get('cbl_cookieVal', false); if(! $val){ $val = uniqid(); wfConfig::set('cbl_cookieVal', $val); } return $val; } public function setCBLCookieBypass(){ wfUtils::setcookie('wfCBLBypass', self::getCBLCookieVal(), time() + (86400 * 365), '/', null, null, true); } public function isCBLBypassCookieSet(){ if(isset($_COOKIE['wfCBLBypass']) && $_COOKIE['wfCBLBypass'] == wfConfig::get('cbl_cookieVal')){ return true; } return false; } 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"); } } } } } private function takeBlockingAction($configVar, $reason){ if($this->googleSafetyCheckOK()){ $action = wfConfig::get($configVar . '_action'); if(! $action){ //error_log("Wordfence action missing for configVar: $configVar"); return; } $secsToGo = 0; if($action == 'block'){ $IP = wfUtils::getIP(); $this->blockIP($IP, $reason); $secsToGo = wfConfig::get('blockedTime'); //Moved the following code AFTER the block to prevent multiple emails. if(wfConfig::get('alertOn_block')){ wordfence::alert("Blocking IP $IP", "Wordfence has blocked IP address $IP.\nThe reason is: \"$reason\".", $IP); } wordfence::status(2, 'info', "Blocking IP $IP. $reason"); } else if($action == 'throttle'){ $IP = wfUtils::getIP(); $this->getDB()->queryWrite("insert into " . $this->throttleTable . " (IP, startTime, endTime, timesThrottled, lastReason) values (%s, unix_timestamp(), unix_timestamp(), 1, '%s') ON DUPLICATE KEY UPDATE endTime=unix_timestamp(), timesThrottled = timesThrottled + 1, lastReason='%s'", wfUtils::inet_pton($IP), $reason, $reason); wordfence::status(2, 'info', "Throttling IP $IP. $reason"); wfConfig::inc('totalIPsThrottled'); $secsToGo = 60; } $this->do503($secsToGo, $reason); } else { return; } } /** * Test if the current request is for wp-login.php or xmlrpc.php * * @return boolean */ private static function isAuthRequest() { if ((strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false)) { return true; } return false; } public function do503($secsToGo, $reason){ $this->initLogRequest(); $this->currentRequest->statusCode = 503; if (!$this->currentRequest->action) { $this->currentRequest->action = 'blocked:wordfence'; } if (!$this->currentRequest->actionDescription) { $this->currentRequest->actionDescription = "blocked: " . $reason; } $this->logHit(); wfConfig::inc('total503s'); wfUtils::doNotCache(); header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); if($secsToGo){ header('Retry-After: ' . $secsToGo); } require_once('wf503.php'); exit(); } private function redirect($URL){ wfUtils::doNotCache(); wp_redirect($URL, 302); exit(); } private function googleSafetyCheckOK(){ //returns true if OK to block. Returns false if we must not block. $cacheKey = md5( (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '') . ' ' . wfUtils::getIP()); //Cache so we can call this multiple times in one request if(! isset(self::$gbSafeCache[$cacheKey])){ $nb = wfConfig::get('neverBlockBG'); if($nb == 'treatAsOtherCrawlers'){ self::$gbSafeCache[$cacheKey] = true; //OK to block because we're treating google like everyone else } else if($nb == 'neverBlockUA' || $nb == 'neverBlockVerified'){ if(wfCrawl::isGoogleCrawler()){ //Check the UA using regex if($nb == 'neverBlockVerified'){ if(wfCrawl::isVerifiedGoogleCrawler(wfUtils::getIP())){ //UA check passed, now verify using PTR if configured to self::$gbSafeCache[$cacheKey] = false; //This is a verified Google crawler, so no we can't block it } else { self::$gbSafeCache[$cacheKey] = true; //This is a crawler claiming to be Google but it did not verify } } else { //neverBlockUA self::$gbSafeCache[$cacheKey] = false; //User configured us to only do a UA check and this claims to be google so don't block } } else { self::$gbSafeCache[$cacheKey] = true; //This isn't a Google UA, so it's OK to block } } else { //error_log("Wordfence error: neverBlockBG option is not set."); self::$gbSafeCache[$cacheKey] = false; //Oops the config option is not set. This should never happen because it's set on install. So we return false to indicate it's not OK to block just for safety. } } if(! isset(self::$gbSafeCache[$cacheKey])){ //error_log("Wordfence assertion fail in googleSafetyCheckOK: cached value is not set."); return false; //for safety } return self::$gbSafeCache[$cacheKey]; //return cached value } public function addStatus($level, $type, $msg){ //$msg = '[' . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . '] ' . $msg; $this->getDB()->queryWrite("insert into " . $this->statusTable . " (ctime, level, type, msg) values (%s, %d, '%s', '%s')", sprintf('%.6f', microtime(true)), $level, $type, $msg); } public function getStatusEvents($lastCtime){ if($lastCtime < 1){ $lastCtime = $this->getDB()->querySingle("select ctime from " . $this->statusTable . " order by ctime desc limit 1000,1"); if(! $lastCtime){ $lastCtime = 0; } } $results = $this->getDB()->querySelect("select ctime, level, type, msg from " . $this->statusTable . " where ctime > %f order by ctime asc", $lastCtime); $timeOffset = 3600 * get_option('gmt_offset'); foreach($results as &$rec){ //$rec['timeAgo'] = wfUtils::makeTimeAgo(time() - $rec['ctime']); $rec['date'] = date('M d H:i:s', $rec['ctime'] + $timeOffset); $rec['msg'] = wp_kses_data( (string) $rec['msg']); } return $results; } public function getSummaryEvents(){ $results = $this->getDB()->querySelect("select ctime, level, type, msg from " . $this->statusTable . " where level = 10 order by ctime desc limit 100"); $timeOffset = 3600 * get_option('gmt_offset'); foreach($results as &$rec){ $rec['date'] = date('M d H:i:s', $rec['ctime'] + $timeOffset); if(strpos($rec['msg'], 'SUM_PREP:') === 0){ break; } } return array_reverse($results); } /** * @return string */ public function getGooglePattern() { return $this->googlePattern; } } /** * */ class wfUserIPRange { /** * @var string|null */ private $ip_string; /** * @param string|null $ip_string */ public function __construct($ip_string = null) { $this->setIPString($ip_string); } /** * Check if the supplied IP address is within the user supplied range. * * @param string $ip * @return bool */ public function isIPInRange($ip) { $ip_string = $this->getIPString(); // IPv4 range if (strpos($ip_string, '.') !== false && strpos($ip, '.') !== false) { // IPv4-mapped-IPv6 if (preg_match('/:ffff:([^:]+)$/i', $ip_string, $matches)) { $ip_string = $matches[1]; } if (preg_match('/:ffff:([^:]+)$/i', $ip, $matches)) { $ip = $matches[1]; } // Range check if (preg_match('/\[\d+\-\d+\]/', $ip_string)) { $IPparts = explode('.', $ip); $whiteParts = explode('.', $ip_string); $mismatch = false; for ($i = 0; $i <= 3; $i++) { if (preg_match('/^\[(\d+)\-(\d+)\]$/', $whiteParts[$i], $m)) { if ($IPparts[$i] < $m[1] || $IPparts[$i] > $m[2]) { $mismatch = true; } } else if ($whiteParts[$i] != $IPparts[$i]) { $mismatch = true; } } if ($mismatch === false) { return true; // Is whitelisted because we did not get a mismatch } } else if ($ip_string == $ip) { return true; } // IPv6 range } else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) { $ip = strtolower(wfUtils::expandIPv6Address($ip)); $ip_string = strtolower(self::expandIPv6Range($ip_string)); if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/i', $ip_string)) { $IPparts = explode(':', $ip); $whiteParts = explode(':', $ip_string); $mismatch = false; for ($i = 0; $i <= 7; $i++) { if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) { $ip_group = hexdec($IPparts[$i]); $range_group_from = hexdec($m[1]); $range_group_to = hexdec($m[2]); if ($ip_group < $range_group_from || $ip_group > $range_group_to) { $mismatch = true; break; } } else if ($whiteParts[$i] != $IPparts[$i]) { $mismatch = true; break; } } if ($mismatch === false) { return true; // Is whitelisted because we did not get a mismatch } } else if ($ip_string == $ip) { return true; } } return false; } /** * Return a set of where clauses to use in MySQL. * * @param string $column * @return false|null|string */ public function toSQL($column = 'ip') { /** @var wpdb $wpdb */ global $wpdb; $ip_string = $this->getIPString(); if (strpos($ip_string, '.') !== false && preg_match('/\[\d+\-\d+\]/', $ip_string)) { $whiteParts = explode('.', $ip_string); $sql = "(SUBSTR($column, 1, 12) = LPAD(CHAR(0xff, 0xff), 12, CHAR(0)) AND "; for ($i = 0, $j = 24; $i <= 3; $i++, $j -= 8) { // MySQL can only perform bitwise operations on integers $conv = sprintf('CAST(CONV(HEX(SUBSTR(%s, 13, 8)), 16, 10) as UNSIGNED INTEGER)', $column); if (preg_match('/^\[(\d+)\-(\d+)\]$/', $whiteParts[$i], $m)) { $sql .= $wpdb->prepare("$conv >> $j & 0xFF BETWEEN %d AND %d", $m[1], $m[2]); } else { $sql .= $wpdb->prepare("$conv >> $j & 0xFF = %d", $whiteParts[$i]); } $sql .= ' AND '; } $sql = substr($sql, 0, -5) . ')'; return $sql; } else if (strpos($ip_string, ':') !== false) { $ip_string = strtolower(self::expandIPv6Range($ip_string)); if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/i', $ip_string)) { $whiteParts = explode(':', $ip_string); $sql = '('; for ($i = 0; $i <= 7; $i++) { // MySQL can only perform bitwise operations on integers $conv = sprintf('CAST(CONV(HEX(SUBSTR(%s, %d, 8)), 16, 10) as UNSIGNED INTEGER)', $column, $i < 4 ? 1 : 9); $j = 16 * (3 - ($i % 4)); if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) { $sql .= $wpdb->prepare("$conv >> $j & 0xFFFF BETWEEN 0x%x AND 0x%x", hexdec($m[1]), hexdec($m[2])); } else { $sql .= $wpdb->prepare("$conv >> $j & 0xFFFF = 0x%x", hexdec($whiteParts[$i])); } $sql .= ' AND '; } $sql = substr($sql, 0, -5) . ')'; return $sql; } } return $wpdb->prepare("($column = %s)", wfUtils::inet_pton($ip_string)); } /** * Expand a compressed printable range representation of an IPv6 address. * * @todo Hook up exceptions for better error handling. * @todo Allow IPv4 mapped IPv6 addresses (::ffff:192.168.1.1). * @param string $ip_range * @return string */ public static function expandIPv6Range($ip_range) { $colon_count = substr_count($ip_range, ':'); $dbl_colon_count = substr_count($ip_range, '::'); if ($dbl_colon_count > 1) { return false; } $dbl_colon_pos = strpos($ip_range, '::'); if ($dbl_colon_pos !== false) { $ip_range = str_replace('::', str_repeat(':0000', (($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($ip_range) - 2) ? 9 : 8) - $colon_count) . ':', $ip_range); $ip_range = trim($ip_range, ':'); } $colon_count = substr_count($ip_range, ':'); if ($colon_count != 7) { return false; } $groups = explode(':', $ip_range); $expanded = ''; foreach ($groups as $group) { if (preg_match('/\[([a-f0-9]{1,4})\-([a-f0-9]{1,4})\]/i', $group, $matches)) { $expanded .= sprintf('[%s-%s]', str_pad(strtolower($matches[1]), 4, '0', STR_PAD_LEFT), str_pad(strtolower($matches[2]), 4, '0', STR_PAD_LEFT)) . ':'; } else if (preg_match('/[a-f0-9]{1,4}/i', $group)) { $expanded .= str_pad(strtolower($group), 4, '0', STR_PAD_LEFT) . ':'; } else { return false; } } return trim($expanded, ':'); } /** * @return bool */ public function isValidRange() { return $this->isValidIPv4Range() || $this->isValidIPv6Range(); } /** * @return bool */ public function isValidIPv4Range() { $ip_string = $this->getIPString(); if (preg_match_all('/(\d+)/', $ip_string, $matches) > 0) { foreach ($matches[1] as $match) { $group = (int) $match; if ($group > 255 || $group < 0) { return false; } } } $group_regex = '([0-9]{1,3}|\[[0-9]{1,3}\-[0-9]{1,3}\])'; return preg_match('/^' . str_repeat("$group_regex.", 3) . $group_regex . '$/i', $ip_string) > 0; } /** * @return bool */ public function isValidIPv6Range() { $ip_string = $this->getIPString(); if (strpos($ip_string, '::') !== false) { $ip_string = self::expandIPv6Range($ip_string); } if (!$ip_string) { return false; } $group_regex = '([a-f0-9]{1,4}|\[[a-f0-9]{1,4}\-[a-f0-9]{1,4}\])'; return preg_match('/^' . str_repeat("$group_regex:", 7) . $group_regex . '$/i', $ip_string) > 0; } /** * @return string|null */ public function getIPString() { return $this->ip_string; } /** * @param string|null $ip_string */ public function setIPString($ip_string) { $this->ip_string = strtolower(preg_replace('/[\x{2013}-\x{2015}]/u', '-', $ip_string)); //Replace em-dash, en-dash, and horizontal bar with a regular dash } } /** * The function of this class is to detect admin users created via direct access to the database (in other words, not * through WordPress). */ class wfAdminUserMonitor { public function isEnabled() { $enabled = wfConfig::get('scansEnabled_suspiciousAdminUsers'); if ($enabled && is_multisite()) { if (!function_exists('wp_is_large_network')) { require_once ABSPATH . WPINC . '/ms-functions.php'; } $enabled = !wp_is_large_network('sites') && !wp_is_large_network('users'); } return $enabled; } /** * */ public function createInitialList() { $admins = $this->getCurrentAdmins(); wfConfig::set_ser('adminUserList', $admins); } /** * @param int $userID */ public function grantSuperAdmin($userID = null) { if ($userID) { $this->addAdmin($userID); } } /** * @param int $userID */ public function revokeSuperAdmin($userID = null) { if ($userID) { $this->removeAdmin($userID); } } /** * @param int $ID * @param mixed $role * @param mixed $old_roles */ public function updateToUserRole($ID = null, $role = null, $old_roles = null) { $admins = $this->getLoggedAdmins(); if ($role !== 'administrator' && array_key_exists($ID, $admins)) { $this->removeAdmin($ID); } else if ($role === 'administrator') { $this->addAdmin($ID); } } /** * @return array|bool */ public function checkNewAdmins() { $loggedAdmins = $this->getLoggedAdmins(); $admins = $this->getCurrentAdmins(); $suspiciousAdmins = array(); foreach ($admins as $adminID => $v) { if (!array_key_exists($adminID, $loggedAdmins)) { $suspiciousAdmins[] = $adminID; } } return $suspiciousAdmins ? $suspiciousAdmins : false; } /** * Checks if the supplied user ID is suspicious. * * @param int $userID * @return bool */ public function isAdminUserLogged($userID) { $loggedAdmins = $this->getLoggedAdmins(); return array_key_exists($userID, $loggedAdmins); } /** * @return array */ public function getCurrentAdmins() { require_once ABSPATH . WPINC . '/user.php'; if (is_multisite()) { if (function_exists("get_sites")) { $sites = get_sites(array( 'network_id' => null, )); } else { $sites = wp_get_sites(array( 'network_id' => null, )); } } else { $sites = array(array( 'blog_id' => get_current_blog_id(), )); } // not very efficient, but the WordPress API doesn't provide a good way to do this. $admins = array(); foreach ($sites as $siteRow) { $siteRowArray = (array) $siteRow; $user_query = new WP_User_Query(array( 'blog_id' => $siteRowArray['blog_id'], 'role' => 'administrator', )); $users = $user_query->get_results(); if (is_array($users)) { /** @var WP_User $user */ foreach ($users as $user) { $admins[$user->ID] = 1; } } } // Add any super admins that aren't also admins on a network $superAdmins = get_super_admins(); foreach ($superAdmins as $userLogin) { $user = get_user_by('login', $userLogin); if ($user) { $admins[$user->ID] = 1; } } return $admins; } public function getLoggedAdmins() { $loggedAdmins = wfConfig::get_ser('adminUserList', false); if (!is_array($loggedAdmins)) { $this->createInitialList(); $loggedAdmins = wfConfig::get_ser('adminUserList', false); } if (!is_array($loggedAdmins)) { $loggedAdmins = array(); } return $loggedAdmins; } /** * @param int $userID */ public function addAdmin($userID) { $loggedAdmins = $this->getLoggedAdmins(); if (!array_key_exists($userID, $loggedAdmins)) { $loggedAdmins[$userID] = 1; wfConfig::set_ser('adminUserList', $loggedAdmins); } } /** * @param int $userID */ public function removeAdmin($userID) { $loggedAdmins = $this->getLoggedAdmins(); if (array_key_exists($userID, $loggedAdmins) && !array_key_exists($userID, $this->getCurrentAdmins())) { unset($loggedAdmins[$userID]); wfConfig::set_ser('adminUserList', $loggedAdmins); } } } /** * */ class wfRequestModel extends wfModel { private static $actionDataEncodedParams = array( 'paramKey', 'paramValue', 'path', ); /** * @param $actionData * @return mixed|string|void */ public static function serializeActionData($actionData) { if (is_array($actionData)) { foreach (self::$actionDataEncodedParams as $key) { if (array_key_exists($key, $actionData)) { $actionData[$key] = base64_encode($actionData[$key]); } } } return json_encode($actionData); } /** * @param $actionDataJSON * @return mixed|string|void */ public static function unserializeActionData($actionDataJSON) { $actionData = json_decode($actionDataJSON, true); if (is_array($actionData)) { foreach (self::$actionDataEncodedParams as $key) { if (array_key_exists($key, $actionData)) { $actionData[$key] = base64_decode($actionData[$key]); } } } return $actionData; } private $columns = array( 'id', 'attackLogTime', 'ctime', 'IP', 'jsRun', 'statusCode', 'isGoogle', 'userID', 'newVisit', 'URL', 'referer', 'UA', 'action', 'actionDescription', 'actionData', ); public function getIDColumn() { return 'id'; } public function getTable() { return $this->getDB()->base_prefix . 'wfHits'; } public function hasColumn($column) { return in_array($column, $this->columns); } } class wfLiveTrafficQuery { protected $validParams = array( 'id' => 'h.id', 'ctime' => 'h.ctime', 'ip' => 'h.ip', 'jsrun' => 'h.jsrun', 'statuscode' => 'h.statuscode', 'isgoogle' => 'h.isgoogle', 'userid' => 'h.userid', 'newvisit' => 'h.newvisit', 'url' => 'h.url', 'referer' => 'h.referer', 'ua' => 'h.ua', 'action' => 'h.action', 'actiondescription' => 'h.actiondescription', 'actiondata' => 'h.actiondata', // wfLogins 'user_login' => 'u.user_login', 'username' => 'l.username', ); /** @var wfLiveTrafficQueryFilterCollection */ private $filters = array(); /** @var wfLiveTrafficQueryGroupBy */ private $groupBy; /** * @var float|null */ private $startDate; /** * @var float|null */ private $endDate; /** * @var int */ private $limit; /** * @var int */ private $offset; private $tableName; /** @var wfLog */ private $wfLog; /** * wfLiveTrafficQuery constructor. * * @param wfLog $wfLog * @param wfLiveTrafficQueryFilterCollection $filters * @param wfLiveTrafficQueryGroupBy $groupBy * @param float $startDate * @param float $endDate * @param int $limit * @param int $offset */ public function __construct($wfLog, $filters = null, $groupBy = null, $startDate = null, $endDate = null, $limit = 20, $offset = 0) { $this->wfLog = $wfLog; $this->filters = $filters; $this->groupBy = $groupBy; $this->startDate = $startDate; $this->endDate = $endDate; $this->limit = $limit; $this->offset = $offset; } /** * @return array|null|object */ public function execute() { global $wpdb; $sql = $this->buildQuery(); $results = $wpdb->get_results($sql, ARRAY_A); $this->getWFLog()->processGetHitsResults('', $results); $verifyCrawlers = false; if ($this->filters !== null && count($this->filters->getFilters()) > 0) { $filters = $this->filters->getFilters(); foreach ($filters as $f) { if (strtolower($f->getParam()) == "isgoogle") { $verifyCrawlers = true; break; } } } foreach ($results as $key => &$row) { if ($row['isGoogle'] && $verifyCrawlers) { if (!wfCrawl::isVerifiedGoogleCrawler($row['IP'], $row['UA'])) { unset($results[$key]); //foreach copies $results and iterates on the copy, so it is safe to mutate $results within the loop continue; } } $row['actionData'] = (array) json_decode($row['actionData'], true); } return array_values($results); } /** * @return string * @throws wfLiveTrafficQueryException */ public function buildQuery() { global $wpdb; $filters = $this->getFilters(); $groupBy = $this->getGroupBy(); $startDate = $this->getStartDate(); $endDate = $this->getEndDate(); $limit = absint($this->getLimit()); $offset = absint($this->getOffset()); $wheres = array("h.action != 'logged:waf'", "h.action != 'scan:detectproxy'"); if ($startDate) { $wheres[] = $wpdb->prepare('h.ctime > %f', $startDate); } if ($endDate) { $wheres[] = $wpdb->prepare('h.ctime < %f', $endDate); } if ($filters instanceof wfLiveTrafficQueryFilterCollection) { $filtersSQL = $filters->toSQL(); if ($filtersSQL) { $wheres[] = $filtersSQL; } } $where = join(' AND ', $wheres); $orderBy = 'ORDER BY h.ctime DESC'; $select = ''; $groupBySQL = ''; if ($groupBy && $groupBy->validate()) { $groupBySQL = "GROUP BY {$groupBy->getParam()}"; $orderBy = 'ORDER BY hitCount DESC'; $select .= ', COUNT(h.id) as hitCount'; } if ($where) { $where = 'WHERE ' . $where; } if (!$limit || $limit > 1000) { $limit = 20; } $limitSQL = $wpdb->prepare('LIMIT %d, %d', $offset, $limit); $sql = <<<SQL SELECT h.*, u.display_name, l.username{$select} FROM {$this->getTableName()} h LEFT JOIN {$wpdb->users} u on h.userID = u.ID LEFT JOIN {$wpdb->base_prefix}wfLogins l on h.id = l.hitID $where $groupBySQL $orderBy $limitSQL SQL; return $sql; } /** * @param $param * @return bool */ public function isValidParam($param) { return array_key_exists(strtolower($param), $this->validParams); } /** * @param $getParam * @return bool|string */ public function getColumnFromParam($getParam) { $getParam = strtolower($getParam); if (array_key_exists($getParam, $this->validParams)) { return $this->validParams[$getParam]; } return false; } /** * @return wfLiveTrafficQueryFilterCollection */ public function getFilters() { return $this->filters; } /** * @param wfLiveTrafficQueryFilterCollection $filters */ public function setFilters($filters) { $this->filters = $filters; } /** * @return float|null */ public function getStartDate() { return $this->startDate; } /** * @param float|null $startDate */ public function setStartDate($startDate) { $this->startDate = $startDate; } /** * @return float|null */ public function getEndDate() { return $this->endDate; } /** * @param float|null $endDate */ public function setEndDate($endDate) { $this->endDate = $endDate; } /** * @return wfLiveTrafficQueryGroupBy */ public function getGroupBy() { return $this->groupBy; } /** * @param wfLiveTrafficQueryGroupBy $groupBy */ public function setGroupBy($groupBy) { $this->groupBy = $groupBy; } /** * @return int */ public function getLimit() { return $this->limit; } /** * @param int $limit */ public function setLimit($limit) { $this->limit = $limit; } /** * @return int */ public function getOffset() { return $this->offset; } /** * @param int $offset */ public function setOffset($offset) { $this->offset = $offset; } /** * @return string */ public function getTableName() { if ($this->tableName === null) { global $wpdb; $this->tableName = $wpdb->base_prefix . 'wfHits'; } return $this->tableName; } /** * @param string $tableName */ public function setTableName($tableName) { $this->tableName = $tableName; } /** * @return wfLog */ public function getWFLog() { return $this->wfLog; } /** * @param wfLog $wfLog */ public function setWFLog($wfLog) { $this->wfLog = $wfLog; } } class wfLiveTrafficQueryFilterCollection { private $filters = array(); /** * wfLiveTrafficQueryFilterCollection constructor. * * @param array $filters */ public function __construct($filters = array()) { $this->filters = $filters; } public function toSQL() { $params = array(); $sql = ''; $filters = $this->getFilters(); if ($filters) { /** @var wfLiveTrafficQueryFilter $filter */ foreach ($filters as $filter) { $params[$filter->getParam()][] = $filter; } } foreach ($params as $param => $filters) { // $sql .= '('; $filtersSQL = ''; foreach ($filters as $filter) { $filterSQL = $filter->toSQL(); if ($filterSQL) { $filtersSQL .= $filterSQL . ' OR '; } } if ($filtersSQL) { $sql .= '(' . substr($filtersSQL, 0, -4) . ') AND '; } } if ($sql) { $sql = substr($sql, 0, -5); } return $sql; } public function addFilter($filter) { $this->filters[] = $filter; } /** * @return array */ public function getFilters() { return $this->filters; } /** * @param array $filters */ public function setFilters($filters) { $this->filters = $filters; } } class wfLiveTrafficQueryFilter { private $param; private $operator; private $value; protected $validOperators = array( '=', '!=', 'contains', 'match', ); /** * @var wfLiveTrafficQuery */ private $query; /** * wfLiveTrafficQueryFilter constructor. * * @param wfLiveTrafficQuery $query * @param string $param * @param string $operator * @param string $value */ public function __construct($query, $param, $operator, $value) { $this->query = $query; $this->param = $param; $this->operator = $operator; $this->value = $value; } /** * @return string|void */ public function toSQL() { $sql = ''; if ($this->validate()) { /** @var wpdb $wpdb */ global $wpdb; $operator = $this->getOperator(); $param = $this->getQuery()->getColumnFromParam($this->getParam()); if (!$param) { return $sql; } $value = $this->getValue(); switch ($operator) { case 'contains': $like = addcslashes($value, '_%\\'); $sql = $wpdb->prepare("$param LIKE %s", "%$like%"); break; case 'match': $sql = $wpdb->prepare("$param LIKE %s", $value); break; default: $sql = $wpdb->prepare("$param $operator %s", $value); break; } } return $sql; } /** * @return bool */ public function validate() { $valid = $this->isValidParam($this->getParam()) && $this->isValidOperator($this->getOperator()); if (defined('WP_DEBUG') && WP_DEBUG) { if (!$valid) { throw new wfLiveTrafficQueryException("Invalid param/operator [{$this->getParam()}]/[{$this->getOperator()}] passed to " . get_class($this)); } return true; } return $valid; } /** * @param string $param * @return bool */ public function isValidParam($param) { return $this->getQuery() && $this->getQuery()->isValidParam($param); } /** * @param string $operator * @return bool */ public function isValidOperator($operator) { return in_array($operator, $this->validOperators); } /** * @return mixed */ public function getParam() { return $this->param; } /** * @param mixed $param */ public function setParam($param) { $this->param = $param; } /** * @return mixed */ public function getOperator() { return $this->operator; } /** * @param mixed $operator */ public function setOperator($operator) { $this->operator = $operator; } /** * @return mixed */ public function getValue() { return $this->value; } /** * @param mixed $value */ public function setValue($value) { $this->value = $value; } /** * @return wfLiveTrafficQuery */ public function getQuery() { return $this->query; } /** * @param wfLiveTrafficQuery $query */ public function setQuery($query) { $this->query = $query; } } class wfLiveTrafficQueryGroupBy { private $param; /** * @var wfLiveTrafficQuery */ private $query; /** * wfLiveTrafficQueryGroupBy constructor. * * @param wfLiveTrafficQuery $query * @param string $param */ public function __construct($query, $param) { $this->query = $query; $this->param = $param; } /** * @return bool * @throws wfLiveTrafficQueryException */ public function validate() { $valid = $this->isValidParam($this->getParam()); if (defined('WP_DEBUG') && WP_DEBUG) { if (!$valid) { throw new wfLiveTrafficQueryException("Invalid param [{$this->getParam()}] passed to " . get_class($this)); } return true; } return $valid; } /** * @param string $param * @return bool */ public function isValidParam($param) { return $this->getQuery() && $this->getQuery()->isValidParam($param); } /** * @return wfLiveTrafficQuery */ public function getQuery() { return $this->query; } /** * @param wfLiveTrafficQuery $query */ public function setQuery($query) { $this->query = $query; } /** * @return mixed */ public function getParam() { return $this->param; } /** * @param mixed $param */ public function setParam($param) { $this->param = $param; } } class wfLiveTrafficQueryException extends Exception { } class wfErrorLogHandler { public static function getErrorLogs($deepSearch = false) { static $errorLogs = null; if ($errorLogs === null) { $searchPaths = array(ABSPATH, ABSPATH . 'wp-admin', ABSPATH . 'wp-content'); $homePath = get_home_path(); if (!in_array($homePath, $searchPaths)) { $searchPaths[] = $homePath; } $errorLogPath = ini_get('error_log'); if (!empty($errorLogPath) && !in_array($errorLogPath, $searchPaths)) { $searchPaths[] = $errorLogPath; } $errorLogs = array(); foreach ($searchPaths as $s) { $errorLogs = array_merge($errorLogs, self::_scanForLogs($s, $deepSearch)); } } return $errorLogs; } private static function _scanForLogs($path, $deepSearch = false) { static $processedFolders = array(); //Protection for endless loops caused by symlinks if (is_file($path)) { $file = basename($path); if (preg_match('#(?:error_log(\-\d+)?$|\.log$)#i', $file)) { return array($path => is_readable($path)); } return array(); } $path = untrailingslashit($path); $contents = @scandir($path); if (!is_array($contents)) { return array(); } $processedFolders[$path] = true; $errorLogs = array(); foreach ($contents as $name) { if ($name == '.' || $name == '..') { continue; } $testPath = $path . DIRECTORY_SEPARATOR . $name; if (!array_key_exists($testPath, $processedFolders)) { if ((is_dir($testPath) && $deepSearch) || !is_dir($testPath)) { $errorLogs = array_merge($errorLogs, self::_scanForLogs($testPath, $deepSearch)); } } } return $errorLogs; } public static function outputErrorLog($path) { $errorLogs = self::getErrorLogs(); if (!isset($errorLogs[$path])) { //Only allow error logs we've identified status_header(404); nocache_headers(); $template = get_404_template(); if ($template && file_exists($template)) { include($template); } exit; } $fh = @fopen($path, 'r'); if (!$fh) { status_header(503); nocache_headers(); echo "503 Service Unavailable"; exit; } $headersOutputted = false; while (!feof($fh)) { $data = fread($fh, 1 * 1024 * 1024); //read 1 megs max per chunk if ($data === false) { //Handle the error where the file was reported readable but we can't actually read it status_header(503); nocache_headers(); echo "503 Service Unavailable"; exit; } if (!$headersOutputted) { header('Content-Type: text/plain'); header('Content-Disposition: attachment; filename="' . basename($path)); $headersOutputted = true; } echo $data; } exit; } }