Wordfence Security – Firewall & Malware Scan - Version 3.4.1

Version Description

  • Removed wfscan.php script and now using pseudo-ajax calls to fire off scans. Much more reliable.
  • Removed visitor.php script and now using pseudo-ajax calls to log human visits.
  • Added config option to allow admin to specify max execution time (advanced only!!).
  • Fixed issue that caused API calls to fail on MultiSite installs.
  • Fixed issue that caused comments to break on MultiSite installs under certain conditions.
  • Fixed issue that caused incorrect domain to be shown in live traffic view on multi-site installs.
  • Fixed issue where some proxies/firewalls send space delimited IP addresses in HTTP headers and Wordfence now handles that.
  • Fixed issue that caused Wordfence to capture activation errors of other plugins.
  • Geo IP database update to November 7th edition.
Download this release

Release Info

Developer mmaunder
Plugin Icon 128x128 Wordfence Security – Firewall & Malware Scan
Version 3.4.1
Comparing to
See all releases

Code changes from version 3.3.7 to 3.4.1

js/admin.js CHANGED
@@ -932,7 +932,7 @@ window['wordfenceAdmin'] = {
932
  }
933
  }
934
  for(var k in WFSLevels[level].otherParams){
935
- if(! /^(?:apiKey|securityLevel|alertEmails|liveTraf_ignoreUsers|liveTraf_ignoreIPs|liveTraf_ignoreUA|liveTraf_hitsMaxSize|maxMem)$/.test(k)){
936
  jQuery('#' + k).val(WFSLevels[level].otherParams[k]);
937
  }
938
  }
932
  }
933
  }
934
  for(var k in WFSLevels[level].otherParams){
935
+ if(! /^(?:apiKey|securityLevel|alertEmails|liveTraf_ignoreUsers|liveTraf_ignoreIPs|liveTraf_ignoreUA|liveTraf_hitsMaxSize|maxMem|maxExecutionTime)$/.test(k)){
936
  jQuery('#' + k).val(WFSLevels[level].otherParams[k]);
937
  }
938
  }
lib/GeoIP.dat CHANGED
Binary file
lib/menu_options.php CHANGED
@@ -220,6 +220,7 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
220
  <tr><th>Check password strength on profile update</th><td><input type="checkbox" id="other_pwStrengthOnUpdate" class="wfConfigElem" name="other_pwStrengthOnUpdate" value="1" <?php $w->cb('other_pwStrengthOnUpdate'); ?> /></td></tr>
221
  <tr><th>Participate in the Wordfence Security Network</th><td><input type="checkbox" id="other_WFNet" class="wfConfigElem" name="other_WFNet" value="1" <?php $w->cb('other_WFNet'); ?> /></td></tr>
222
  <tr><th>Maximum memory Wordfence can use</th><td><input type="text" id="maxMem" name="maxMem" value="<?php $w->f('maxMem'); ?>" size="4" />Megabytes</td></tr>
 
223
  <tr><th>Enable debugging mode (increases database load)</th><td><input type="checkbox" id="debugOn" class="wfConfigElem" name="debugOn" value="1" <?php $w->cb('debugOn'); ?> /></td></tr>
224
  <tr><th>Delete Wordfence tables and data on deactivation?</th><td><input type="checkbox" id="deleteTablesOnDeact" class="wfConfigElem" name="deleteTablesOnDeact" value="1" <?php $w->cb('deleteTablesOnDeact'); ?> /></td></tr>
225
  <tr><th colspan="2"><a href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=sysinfo&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Click to view your system's configuration in a new window</a></th></tr>
220
  <tr><th>Check password strength on profile update</th><td><input type="checkbox" id="other_pwStrengthOnUpdate" class="wfConfigElem" name="other_pwStrengthOnUpdate" value="1" <?php $w->cb('other_pwStrengthOnUpdate'); ?> /></td></tr>
221
  <tr><th>Participate in the Wordfence Security Network</th><td><input type="checkbox" id="other_WFNet" class="wfConfigElem" name="other_WFNet" value="1" <?php $w->cb('other_WFNet'); ?> /></td></tr>
222
  <tr><th>Maximum memory Wordfence can use</th><td><input type="text" id="maxMem" name="maxMem" value="<?php $w->f('maxMem'); ?>" size="4" />Megabytes</td></tr>
223
+ <tr><th>Maximum execution time for each scan stage</th><td><input type="text" id="maxExecutionTime" name="maxExecutionTime" value="<?php $w->f('maxExecutionTime'); ?>" size="4" />Blank for default. Must be greater than 10.</td></tr>
224
  <tr><th>Enable debugging mode (increases database load)</th><td><input type="checkbox" id="debugOn" class="wfConfigElem" name="debugOn" value="1" <?php $w->cb('debugOn'); ?> /></td></tr>
225
  <tr><th>Delete Wordfence tables and data on deactivation?</th><td><input type="checkbox" id="deleteTablesOnDeact" class="wfConfigElem" name="deleteTablesOnDeact" value="1" <?php $w->cb('deleteTablesOnDeact'); ?> /></td></tr>
226
  <tr><th colspan="2"><a href="<?php echo wfUtils::siteURLRelative(); ?>?_wfsf=sysinfo&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Click to view your system's configuration in a new window</a></th></tr>
lib/wfAPI.php CHANGED
@@ -155,7 +155,12 @@ class wfAPI {
155
  public function makeAPIQueryString(){
156
  $siteurl = '';
157
  if(function_exists('get_bloginfo')){
158
- $siteurl = get_bloginfo('siteurl');
 
 
 
 
 
159
  }
160
  return self::buildQuery(array(
161
  'v' => $this->wordpressVersion,
155
  public function makeAPIQueryString(){
156
  $siteurl = '';
157
  if(function_exists('get_bloginfo')){
158
+ if(is_multisite()){
159
+ $siteurl = network_home_url();
160
+ $siteurl = rtrim($siteurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
161
+ } else {
162
+ $siteurl = get_bloginfo('siteurl');
163
+ }
164
  }
165
  return self::buildQuery(array(
166
  'v' => $this->wordpressVersion,
lib/wfConfig.php CHANGED
@@ -48,7 +48,7 @@ class wfConfig {
48
  ),
49
  "otherParams" => array(
50
  'securityLevel' => '0',
51
- "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '',
52
  "neverBlockBG" => "neverBlockVerified",
53
  "loginSec_countFailMins" => "5",
54
  "loginSec_lockoutMins" => "5",
@@ -111,7 +111,7 @@ class wfConfig {
111
  ),
112
  "otherParams" => array(
113
  'securityLevel' => '1',
114
- "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '',
115
  "neverBlockBG" => "neverBlockVerified",
116
  "loginSec_countFailMins" => "5",
117
  "loginSec_lockoutMins" => "5",
@@ -174,7 +174,7 @@ class wfConfig {
174
  ),
175
  "otherParams" => array(
176
  'securityLevel' => '2',
177
- "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '',
178
  "neverBlockBG" => "neverBlockVerified",
179
  "loginSec_countFailMins" => "240",
180
  "loginSec_lockoutMins" => "240",
@@ -237,7 +237,7 @@ class wfConfig {
237
  ),
238
  "otherParams" => array(
239
  'securityLevel' => '3',
240
- "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '',
241
  "neverBlockBG" => "neverBlockVerified",
242
  "loginSec_countFailMins" => "1440",
243
  "loginSec_lockoutMins" => "1440",
@@ -300,7 +300,7 @@ class wfConfig {
300
  ),
301
  "otherParams" => array(
302
  'securityLevel' => '4',
303
- "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '',
304
  "neverBlockBG" => "neverBlockVerified",
305
  "loginSec_countFailMins" => "1440",
306
  "loginSec_lockoutMins" => "1440",
48
  ),
49
  "otherParams" => array(
50
  'securityLevel' => '0',
51
+ "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '', 'maxExecutionTime' => '',
52
  "neverBlockBG" => "neverBlockVerified",
53
  "loginSec_countFailMins" => "5",
54
  "loginSec_lockoutMins" => "5",
111
  ),
112
  "otherParams" => array(
113
  'securityLevel' => '1',
114
+ "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '', 'maxExecutionTime' => '',
115
  "neverBlockBG" => "neverBlockVerified",
116
  "loginSec_countFailMins" => "5",
117
  "loginSec_lockoutMins" => "5",
174
  ),
175
  "otherParams" => array(
176
  'securityLevel' => '2',
177
+ "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '', 'maxExecutionTime' => '',
178
  "neverBlockBG" => "neverBlockVerified",
179
  "loginSec_countFailMins" => "240",
180
  "loginSec_lockoutMins" => "240",
237
  ),
238
  "otherParams" => array(
239
  'securityLevel' => '3',
240
+ "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '', 'maxExecutionTime' => '',
241
  "neverBlockBG" => "neverBlockVerified",
242
  "loginSec_countFailMins" => "1440",
243
  "loginSec_lockoutMins" => "1440",
300
  ),
301
  "otherParams" => array(
302
  'securityLevel' => '4',
303
+ "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'whitelisted' => '', 'maxExecutionTime' => '',
304
  "neverBlockBG" => "neverBlockVerified",
305
  "loginSec_countFailMins" => "1440",
306
  "loginSec_lockoutMins" => "1440",
wfscan.php → lib/wfScan.php RENAMED
@@ -1,26 +1,4 @@
1
  <?php
2
- /* Don't remove this line. WFSOURCEVISIBLE */
3
- define('WORDFENCE_VERSIONONLY_MODE', true); //So that we can include wordfence.php and get the version constant
4
- ignore_user_abort(true);
5
- $wordfence_wp_version = false;
6
- if ( !defined('ABSPATH') ) {
7
- /** Set up WordPress environment */
8
- if($_SERVER['SCRIPT_FILENAME']){
9
- $wfBaseDir = preg_replace('/[^\/]+\/[^\/]+\/[^\/]+\/wfscan\.php$/', '', $_SERVER['SCRIPT_FILENAME']);
10
- require_once($wfBaseDir . 'wp-load.php');
11
- global $wp_version;
12
- global $wordfence_wp_version;
13
- require($wfBaseDir . 'wp-includes/version.php');
14
- $wordfence_wp_version = $wp_version;
15
- } else {
16
- require_once('../../../wp-load.php');
17
- require_once('../../../wp-includes/version.php');
18
- }
19
- }
20
- require_once('wordfence.php');
21
- require_once('lib/wordfenceConstants.php');
22
- require_once('lib/wfScanEngine.php');
23
-
24
  class wfScan {
25
  public static $debugMode = false;
26
  public static $errorHandlingOn = true;
@@ -43,7 +21,7 @@ class wfScan {
43
  self::status(4, 'info', "Scan engine received request.");
44
  self::status(4, 'info', "Checking cronkey");
45
  if(! $_GET['cronKey']){
46
- self::status(4, 'error', "Wordfence wfscan.php accessed directly, or WF did not receive a cronkey.");
47
  echo "If you see this message it means Wordfence is working correctly. You should not access this URL directly. It is part of the Wordfence security plugin and is designed for internal use only.";
48
  exit();
49
  }
@@ -65,7 +43,6 @@ class wfScan {
65
  }
66
  /* --------- end cronkey check ---------- */
67
 
68
- wfUtils::iniSet('max_execution_time', 1800); //30 mins
69
  self::status(4, 'info', "Becoming admin for scan");
70
  self::becomeAdmin();
71
  self::status(4, 'info', "Done become admin");
@@ -217,5 +194,4 @@ class wfScan {
217
  wordfence::status($level, $type, $msg);
218
  }
219
  }
220
- wfScan::wfScanMain();
221
  ?>
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  class wfScan {
3
  public static $debugMode = false;
4
  public static $errorHandlingOn = true;
21
  self::status(4, 'info', "Scan engine received request.");
22
  self::status(4, 'info', "Checking cronkey");
23
  if(! $_GET['cronKey']){
24
+ self::status(4, 'error', "Wordfence scan script accessed directly, or WF did not receive a cronkey.");
25
  echo "If you see this message it means Wordfence is working correctly. You should not access this URL directly. It is part of the Wordfence security plugin and is designed for internal use only.";
26
  exit();
27
  }
43
  }
44
  /* --------- end cronkey check ---------- */
45
 
 
46
  self::status(4, 'info', "Becoming admin for scan");
47
  self::becomeAdmin();
48
  self::status(4, 'info', "Done become admin");
194
  wordfence::status($level, $type, $msg);
195
  }
196
  }
 
197
  ?>
lib/wfScanEngine.php CHANGED
@@ -21,7 +21,7 @@ class wfScanEngine {
21
  private $apiKey = false;
22
  private $startTime = 0;
23
  private $scanStep = 0;
24
- public $maxExecTime = 10; //If more than $maxExecTime has elapsed since last check, fork a new scan process and continue
25
  private $publicScanEnabled = false;
26
  private $fileContentsResults = false;
27
  private $scanner = false;
@@ -41,6 +41,7 @@ class wfScanEngine {
41
  }
42
  public function __construct(){
43
  $this->startTime = time();
 
44
  $this->i = new wfIssues();
45
  $this->i->deleteNew();
46
  $this->cycleStartTime = time();
@@ -851,72 +852,6 @@ class wfScanEngine {
851
  throw new Exception("Scan was killed on administrator request.");
852
  }
853
  }
854
- private static function getOwnHostname(){
855
- if(preg_match('/https?:\/\/([^\/]+)/i', site_url(), $matches)){
856
- $host = $matches[1];
857
- } else {
858
- wordfence::status(2, 'error', "Warning: Could not extract hostname from site URL: " . site_url());
859
- $host = site_url();
860
- }
861
- return $host;
862
- }
863
- private static function tryCronURL(){
864
- if(! wfConfig::get('cronTestID')){
865
- wfConfig::set('cronTestID', wfUtils::bigRandomHex());
866
- }
867
- $URL = wfConfig::get('cronURL');
868
- $sendHeader = wfConfig::get('cronSendHeader');
869
- $opts = array(
870
- 'timeout' => 30, //Long timeout here which is fine because it should return immediately if there are no delays.
871
- 'blocking' => true,
872
- 'sslverify' => false
873
- );
874
- if($sendHeader){
875
- $host = self::getOwnHostname();
876
- $opts['headers'] = array( 'Host' => $host);
877
- }
878
- $testURL = $URL . '?test=1';
879
- wordfence::status(4, 'info', "Testing cron URL: $testURL");
880
- $result = wp_remote_post($testURL, $opts);
881
- if( is_array($result) && isset($result['body']) && preg_match('/WFCRONTESTOK:' . wfConfig::get('cronTestID') . '/', $result['body'])){
882
- wordfence::status(4, 'info', "Cron URL test success with: $testURL");
883
- return true;
884
- } else {
885
- wordfence::status(4, 'info', "Cron URL test fail with: $testURL");
886
- self::$cronTestFailedURLs[] = $testURL;
887
- }
888
- return false;
889
- }
890
- private static function detectCronURL(){
891
- $URL = wfConfig::get('cronURL');
892
- if($URL){
893
- if(self::tryCronURL()){
894
- return true;
895
- }
896
- }
897
-
898
- $host = self::getOwnHostname();
899
- $URLS = array();
900
- $URLS[] = array(false, plugins_url('wordfence/wfscan.php'));
901
- $URLS[] = array(true, preg_replace('/^https?:\/\/[^\/]+/i', 'http://127.0.0.1', $URLS[0][1]));
902
- $URLS[] = array(true, preg_replace('/^https?:\/\/[^\/]+/i', 'https://127.0.0.1', $URLS[0][1]));
903
- $withHostInsecure = 'http://' . $host . '/wp-content/plugins/wordfence/wfscan.php';
904
- $withHostSecure = 'https://' . $host . '/wp-content/plugins/wordfence/wfscan.php';
905
- if($URLS[0][1] != $withHostInsecure){
906
- $URLS[] = array(false, $withHostInsecure);
907
- }
908
- if($URLS[0][1] != $withHostSecure){
909
- $URLS[] = array(false, $withHostSecure);
910
- }
911
- foreach($URLS as $elem){
912
- wfConfig::set('cronSendHeader', $elem[0] ? 1 : 0);
913
- wfConfig::set('cronURL', $elem[1]);
914
- if(self::tryCronURL()){
915
- return true;
916
- }
917
- }
918
- return false;
919
- }
920
  public static function startScan($isFork = false){
921
  if(! $isFork){ //beginning of scan
922
  wfConfig::set('wfKillRequested', 0);
@@ -924,31 +859,17 @@ class wfScanEngine {
924
  if(wfUtils::isScanRunning()){
925
  return "A scan is already running. Use the kill link if you would like to terminate the current scan.";
926
  }
927
- if(! self::detectCronURL()){
928
- $msg = 'We could not determine how this WordPress server connects to itself. Please read <a href="http://www.wordfence.com/docs/wordfence-server-cant-connect-to-itself-error/" target="_blank">the documentation we provide on this page</a> which may help with this error. For your info, this machine\'s hostname is: ' . self::getOwnHostname();
929
- $msg .= "<br /><br />We tried the following URLs:<ul>";
930
- foreach(self::$cronTestFailedURLs as $URL){
931
- $msg .= '<li><a href="' . $URL . '" target="_blank">' . $URL . '</a></li>';
932
- }
933
- $msg .= '</ul>';
934
- return $msg;
935
- }
936
  }
937
 
938
  $cronKey = wfUtils::bigRandomHex();
939
  wfConfig::set('currentCronKey', time() . ',' . $cronKey);
940
- $cronURL = wfConfig::get('cronURL') . '?isFork=' . ($isFork ? '1' : '0') . '&cronKey=' . $cronKey;
 
941
  wordfence::status(4, 'info', "Starting cron at URL $cronURL");
942
  $headers = array();
943
- if(wfConfig::get('cronSendHeader')){
944
- $headers['Host'] = self::getOwnHostname();
945
- }
946
  wordfence::status(4, 'info', "Starting wp_remote_post");
947
- if($isFork){
948
- $timeout = 8; //2 seconds shorter than max execution time which ensures that only 2 HTTP processes are ever occupied
949
- } else {
950
- $timeout = 3; //3 seconds if we're kicking off the scan so that the Ajax call returns quickly and UI isn't too slow
951
- }
952
  $result = wp_remote_post( $cronURL, array(
953
  'timeout' => $timeout, //Must be less than max execution time or more than 2 HTTP children will be occupied by scan
954
  'blocking' => true, //Non-blocking seems to block anyway, so we use blocking
@@ -961,6 +882,16 @@ class wfScanEngine {
961
  public function processResponse($result){
962
  return false;
963
  }
 
 
 
 
 
 
 
 
 
 
964
  }
965
 
966
  ?>
21
  private $apiKey = false;
22
  private $startTime = 0;
23
  private $scanStep = 0;
24
+ public $maxExecTime = false; //If more than $maxExecTime has elapsed since last check, fork a new scan process and continue
25
  private $publicScanEnabled = false;
26
  private $fileContentsResults = false;
27
  private $scanner = false;
41
  }
42
  public function __construct(){
43
  $this->startTime = time();
44
+ $this->maxExecTime = self::getMaxExecutionTime() / 2;
45
  $this->i = new wfIssues();
46
  $this->i->deleteNew();
47
  $this->cycleStartTime = time();
852
  throw new Exception("Scan was killed on administrator request.");
853
  }
854
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
855
  public static function startScan($isFork = false){
856
  if(! $isFork){ //beginning of scan
857
  wfConfig::set('wfKillRequested', 0);
859
  if(wfUtils::isScanRunning()){
860
  return "A scan is already running. Use the kill link if you would like to terminate the current scan.";
861
  }
 
 
 
 
 
 
 
 
 
862
  }
863
 
864
  $cronKey = wfUtils::bigRandomHex();
865
  wfConfig::set('currentCronKey', time() . ',' . $cronKey);
866
+ $cronURL = admin_url('admin-ajax.php');
867
+ $cronURL .= '?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&cronKey=' . $cronKey;
868
  wordfence::status(4, 'info', "Starting cron at URL $cronURL");
869
  $headers = array();
 
 
 
870
  wordfence::status(4, 'info', "Starting wp_remote_post");
871
+ $timeout = self::getMaxExecutionTime() - 2; //2 seconds shorter than max execution time which ensures that only 2 HTTP processes are ever occupied
872
+
 
 
 
873
  $result = wp_remote_post( $cronURL, array(
874
  'timeout' => $timeout, //Must be less than max execution time or more than 2 HTTP children will be occupied by scan
875
  'blocking' => true, //Non-blocking seems to block anyway, so we use blocking
882
  public function processResponse($result){
883
  return false;
884
  }
885
+ public static function getMaxExecutionTime(){
886
+ $maxExecutionTime = wfConfig::get('maxExecutionTime');
887
+ if(! is_numeric($maxExecutionTime)){
888
+ $maxExecutionTime = @ini_get('max_execution_time');
889
+ }
890
+ if(! is_numeric($maxExecutionTime)){
891
+ $maxExecutionTime = 15;
892
+ }
893
+ return $maxExecutionTime;
894
+ }
895
  }
896
 
897
  ?>
lib/wfUtils.php CHANGED
@@ -96,6 +96,15 @@ class wfUtils {
96
  break;
97
  }
98
  }
 
 
 
 
 
 
 
 
 
99
  }
100
  if(preg_match('/:\d+$/', $IP)){
101
  $IP = preg_replace('/:\d+$/', '', $IP);
@@ -136,7 +145,12 @@ class wfUtils {
136
  return false;
137
  }
138
  public static function getRequestedURL(){
139
- return (@$_SERVER['HTTPS'] ? 'https' : 'http') . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
 
 
 
 
 
140
  }
141
 
142
  public static function editUserLink($userID){
96
  break;
97
  }
98
  }
99
+ } else if(preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)\s+(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)){
100
+ $parts = explode(' ', $IP); //Some users have "unknown 100.100.100.100" for example so we take the first thing that looks like an IP.
101
+ foreach($parts as $part){
102
+ if(preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $part)){
103
+ $IP = trim($part);
104
+ break;
105
+ }
106
+ }
107
+
108
  }
109
  if(preg_match('/:\d+$/', $IP)){
110
  $IP = preg_replace('/:\d+$/', '', $IP);
145
  return false;
146
  }
147
  public static function getRequestedURL(){
148
+ if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']){
149
+ $host = $_SERVER['HTTP_HOST'];
150
+ } else {
151
+ $host = $_SERVER['SERVER_NAME'];
152
+ }
153
+ return (@$_SERVER['HTTPS'] ? 'https' : 'http') . '://' . $host . $_SERVER['REQUEST_URI'];
154
  }
155
 
156
  public static function editUserLink($userID){
lib/wordfenceClass.php CHANGED
@@ -217,9 +217,6 @@ class wordfence {
217
  wfConfig::set('alertEmailMsgCount', 0);
218
  }
219
 
220
- @chmod(dirname(__FILE__) . '/../wfscan.php', 0755);
221
- @chmod(dirname(__FILE__) . '/../visitor.php', 0755);
222
-
223
  global $wpdb;
224
  $prefix = $wpdb->base_prefix;
225
  $db->queryIgnoreError("alter table $prefix"."wfConfig modify column val longblob");
@@ -244,6 +241,13 @@ class wordfence {
244
  global $blog_id;
245
  if($blog_id == 1 && get_option('wordfenceActivated') != 1){ return; } //Because the plugin is active once installed, even before it's network activated, for site 1 (WordPress team, why?!)
246
  }
 
 
 
 
 
 
 
247
  add_action('wordfence_start_scheduled_scan', 'wordfence::wordfenceStartScheduledScan');
248
  add_action('wordfence_daily_cron', 'wordfence::dailyCron');
249
  add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
@@ -279,6 +283,27 @@ class wordfence {
279
  }
280
  }
281
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  public static function ajaxReceiver(){
283
  if(! wfUtils::isAdmin()){
284
  die(json_encode(array('errorMsg' => "You appear to have logged out or you are not an admin. Please sign-out and sign-in again.")));
@@ -1132,7 +1157,6 @@ class wordfence {
1132
  }
1133
  private static function wfFunc_testtime(){
1134
  header('Content-Type: text/plain');
1135
- wfUtils::iniSet('max_execution_time', 1800); //30 mins
1136
  @error_reporting(E_ALL);
1137
  wfUtils::iniSet('display_errors','On');
1138
  set_error_handler('wordfence::memtest_error_handler', E_ALL);
@@ -1188,7 +1212,9 @@ class wordfence {
1188
  exit();
1189
  }
1190
  public static function wp_head(){
1191
- echo '<script type="text/javascript">var src="' . wfUtils::getBaseURL() . 'visitor.php?hid=' . wfUtils::encrypt(self::$hitID) . '"; if(window.location.protocol == "https:"){ src = src.replace("http:", "https:"); } var wfHTImg = new Image(); wfHTImg.src=src;</script>';
 
 
1192
  }
1193
  public static function shutdownAction(){
1194
  }
@@ -1427,8 +1453,12 @@ class wordfence {
1427
 
1428
  if(($approved == 1 || $approved == 0) && wfConfig::get('other_scanComments')){
1429
  $wf = new wfScanEngine();
1430
- if($wf->isBadComment($cData['comment_author'], $cData['comment_author_email'], $cData['comment_author_url'], $cData['comment_author_IP'], $cData['comment_content'])){
1431
- return 'spam';
 
 
 
 
1432
  }
1433
  }
1434
  return $approved;
217
  wfConfig::set('alertEmailMsgCount', 0);
218
  }
219
 
 
 
 
220
  global $wpdb;
221
  $prefix = $wpdb->base_prefix;
222
  $db->queryIgnoreError("alter table $prefix"."wfConfig modify column val longblob");
241
  global $blog_id;
242
  if($blog_id == 1 && get_option('wordfenceActivated') != 1){ return; } //Because the plugin is active once installed, even before it's network activated, for site 1 (WordPress team, why?!)
243
  }
244
+ //User may be logged in or not, so register both handlers
245
+ add_action('wp_ajax_nopriv_wordfence_logHuman', 'wordfence::ajax_logHuman_callback');
246
+ add_action('wp_ajax_wordfence_logHuman', 'wordfence::ajax_logHuman_callback');
247
+ add_action('wp_ajax_nopriv_wordfence_doScan', 'wordfence::ajax_doScan_callback');
248
+ add_action('wp_ajax_wordfence_doScan', 'wordfence::ajax_doScan_callback');
249
+
250
+
251
  add_action('wordfence_start_scheduled_scan', 'wordfence::wordfenceStartScheduledScan');
252
  add_action('wordfence_daily_cron', 'wordfence::dailyCron');
253
  add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
283
  }
284
  }
285
  }
286
+ public static function ajax_doScan_callback(){
287
+ ignore_user_abort(true);
288
+ $wordfence_wp_version = false;
289
+ require(ABSPATH . 'wp-includes/version.php');
290
+ $wordfence_wp_version = $wp_version;
291
+ require('wfScan.php');
292
+ wfScan::wfScanMain();
293
+
294
+ } //END doScan
295
+ public static function ajax_logHuman_callback(){
296
+ $hid = $_GET['hid'];
297
+ $hid = wfUtils::decrypt($hid);
298
+ if(! preg_match('/^\d+$/', $hid)){ exit(); }
299
+ $db = new wfDB();
300
+ global $wpdb; $p = $wpdb->base_prefix;
301
+ $db->query("update $p"."wfHits set jsRun=1 where id=%d", $hid);
302
+ if(! headers_sent()){ //suppress content-type warning in chrome
303
+ header('Content-type: image/gif');
304
+ }
305
+ die("");
306
+ }
307
  public static function ajaxReceiver(){
308
  if(! wfUtils::isAdmin()){
309
  die(json_encode(array('errorMsg' => "You appear to have logged out or you are not an admin. Please sign-out and sign-in again.")));
1157
  }
1158
  private static function wfFunc_testtime(){
1159
  header('Content-Type: text/plain');
 
1160
  @error_reporting(E_ALL);
1161
  wfUtils::iniSet('display_errors','On');
1162
  set_error_handler('wordfence::memtest_error_handler', E_ALL);
1212
  exit();
1213
  }
1214
  public static function wp_head(){
1215
+ $URL = admin_url('admin-ajax.php');
1216
+ $URL .= '?action=wordfence_logHuman&hid=' . wfUtils::encrypt(self::$hitID);
1217
+ echo '<script type="text/javascript">var src="' . $URL . '"; if(window.location.protocol == "https:"){ src = src.replace("http:", "https:"); } var wfHTImg = new Image(); wfHTImg.src=src;</script>';
1218
  }
1219
  public static function shutdownAction(){
1220
  }
1453
 
1454
  if(($approved == 1 || $approved == 0) && wfConfig::get('other_scanComments')){
1455
  $wf = new wfScanEngine();
1456
+ try {
1457
+ if($wf->isBadComment($cData['comment_author'], $cData['comment_author_email'], $cData['comment_author_url'], $cData['comment_author_IP'], $cData['comment_content'])){
1458
+ return 'spam';
1459
+ }
1460
+ } catch(Exception $e){
1461
+ //This will most likely be an API exception because we can't contact the API, so we ignore it and let the normal comment mechanisms run.
1462
  }
1463
  }
1464
  return $approved;
lib/wordfenceConstants.php CHANGED
@@ -2,7 +2,7 @@
2
  define('WORDFENCE_API_VERSION', '2.4');
3
  define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
4
  define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
5
- define('WORDFENCE_MAX_SCAN_TIME', 600);
6
  define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
7
  define('WORDFENCE_MAX_IPLOC_AGE', 604800); //1 week
8
  define('WORDFENCE_CRAWLER_VERIFY_CACHE_TIME', 604800);
2
  define('WORDFENCE_API_VERSION', '2.4');
3
  define('WORDFENCE_API_URL_SEC', 'https://noc1.wordfence.com/');
4
  define('WORDFENCE_API_URL_NONSEC', 'http://noc1.wordfence.com/');
5
+ define('WORDFENCE_MAX_SCAN_TIME', 86400); //Increased this from 10 mins to 1 day because very big scans run for a long time. Users can use kill.
6
  define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
7
  define('WORDFENCE_MAX_IPLOC_AGE', 604800); //1 week
8
  define('WORDFENCE_CRAWLER_VERIFY_CACHE_TIME', 604800);
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: wordpress, security, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure
4
  Requires at least: 3.3.1
5
  Tested up to: 3.4.2
6
- Stable tag: 3.3.7
7
 
8
  Wordfence Security is a free enterprise class security plugin that includes a firewall, virus scanning, real-time traffic with geolocation and more.
9
 
@@ -154,6 +154,17 @@ or a theme, because often these have been updated to fix a security hole.
154
 
155
  == Changelog ==
156
 
 
 
 
 
 
 
 
 
 
 
 
157
  = 3.3.7 =
158
  * Upgrade immediately. Fixes possible XSS vulnerability in Wordfence "firewall unlock" form.
159
  * Also added rate limiting to max of 10 requests per second to the unlock form.
3
  Tags: wordpress, security, wordpress security, security plugin, secure, anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence, securty, secrity, secure
4
  Requires at least: 3.3.1
5
  Tested up to: 3.4.2
6
+ Stable tag: 3.4.1
7
 
8
  Wordfence Security is a free enterprise class security plugin that includes a firewall, virus scanning, real-time traffic with geolocation and more.
9
 
154
 
155
  == Changelog ==
156
 
157
+ = 3.4.1 =
158
+ * Removed wfscan.php script and now using pseudo-ajax calls to fire off scans. Much more reliable.
159
+ * Removed visitor.php script and now using pseudo-ajax calls to log human visits.
160
+ * Added config option to allow admin to specify max execution time (advanced only!!).
161
+ * Fixed issue that caused API calls to fail on MultiSite installs.
162
+ * Fixed issue that caused comments to break on MultiSite installs under certain conditions.
163
+ * Fixed issue that caused incorrect domain to be shown in live traffic view on multi-site installs.
164
+ * Fixed issue where some proxies/firewalls send space delimited IP addresses in HTTP headers and Wordfence now handles that.
165
+ * Fixed issue that caused Wordfence to capture activation errors of other plugins.
166
+ * Geo IP database update to November 7th edition.
167
+
168
  = 3.3.7 =
169
  * Upgrade immediately. Fixes possible XSS vulnerability in Wordfence "firewall unlock" form.
170
  * Also added rate limiting to max of 10 requests per second to the unlock form.
visitor.php DELETED
@@ -1,33 +0,0 @@
1
- <?php
2
- define('WORDFENCE_VERSIONONLY_MODE', true); //So that we can include wordfence.php and get the version constant
3
- ignore_user_abort(true);
4
- if ( !defined('ABSPATH') ) {
5
- /** Set up WordPress environment */
6
- if($_SERVER['SCRIPT_FILENAME']){
7
- $wfBaseDir = preg_replace('/[^\/]+\/[^\/]+\/[^\/]+\/visitor\.php$/', '', $_SERVER['SCRIPT_FILENAME']);
8
- require_once($wfBaseDir . 'wp-load.php');
9
- global $wp_version;
10
- global $wordfence_wp_version;
11
- require($wfBaseDir . 'wp-includes/version.php');
12
- $wordfence_wp_version = $wp_version;
13
- } else {
14
- require_once('../../../wp-load.php');
15
- require_once('../../../wp-includes/version.php');
16
- }
17
-
18
- }
19
- require_once('wordfence.php');
20
- require_once('lib/wfUtils.php');
21
- require_once('lib/wfDB.php');
22
- function wfVisitor(){
23
- $hid = $_GET['hid'];
24
- $hid = wfUtils::decrypt($hid);
25
- if(! preg_match('/^\d+$/', $hid)){ exit(); }
26
- $db = new wfDB();
27
- global $wpdb; $p = $wpdb->base_prefix;
28
- $db->query("update $p"."wfHits set jsRun=1 where id=%d", $hid);
29
- exit();
30
- }
31
- wfVisitor();
32
-
33
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
wordfence.php CHANGED
@@ -4,11 +4,13 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://wordfence.com/
5
  Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
6
  Author: Mark Maunder
7
- Version: 3.3.7
8
  Author URI: http://wordfence.com/
9
  */
10
- define('WORDFENCE_VERSION', '3.3.7');
11
- add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
 
 
12
  if(! defined('WORDFENCE_VERSIONONLY_MODE')){
13
  if((int) @ini_get('memory_limit') < 64){
14
  if(strpos(ini_get('disable_functions'), 'ini_set') === false){
4
  Plugin URI: http://wordfence.com/
5
  Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
6
  Author: Mark Maunder
7
+ Version: 3.4.1
8
  Author URI: http://wordfence.com/
9
  */
10
+ define('WORDFENCE_VERSION', '3.4.1');
11
+ if(get_option('wordfenceActivated') != 1){
12
+ add_action('activated_plugin','wordfence_save_activation_error'); function wordfence_save_activation_error(){ update_option('wf_plugin_act_error', ob_get_contents()); }
13
+ }
14
  if(! defined('WORDFENCE_VERSIONONLY_MODE')){
15
  if((int) @ini_get('memory_limit') < 64){
16
  if(strpos(ini_get('disable_functions'), 'ini_set') === false){