Wordfence Security – Firewall & Malware Scan - Version 3.4.4

Version Description

  • Fixed issue that caused scans to not complete.
  • Fixed issue that caused scans to launch a large number of child processes due to very short scan timeout.
  • Fixed issue that caused websites that don't know their own hostname to not be able to scan.
  • Added workaround for a bug in Better WP Security breaking Wordfence due to their code overwriting the WP version.
  • Optimized the way we calculate max execution time for each process while scanning.
Download this release

Release Info

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

Code changes from version 3.4.1 to 3.4.4

lib/menu_options.php CHANGED
@@ -220,7 +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>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>
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 9.</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/wfScanEngine.php CHANGED
@@ -17,7 +17,6 @@ class wfScanEngine {
17
  private $hashes = false;
18
  private $jobList = array();
19
  private $i = false;
20
- private $wp_version = false;
21
  private $apiKey = false;
22
  private $startTime = 0;
23
  private $scanStep = 0;
@@ -41,7 +40,7 @@ class wfScanEngine {
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();
@@ -93,7 +92,9 @@ class wfScanEngine {
93
  }
94
  }
95
  public function fork(){
 
96
  if(wfConfig::set_ser('wfsd_engine', $this, true)){
 
97
  self::startScan(true);
98
  } //Otherwise there was an error so don't start another scan.
99
  exit(0);
@@ -860,37 +861,65 @@ class wfScanEngine {
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
876
  'sslverify' => false,
877
- 'headers' => $headers
878
- ) );
879
- wordfence::status(4, 'info', "Scan process ended after forking.");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
880
  return false; //No error
881
  }
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
 
17
  private $hashes = false;
18
  private $jobList = array();
19
  private $i = false;
 
20
  private $apiKey = false;
21
  private $startTime = 0;
22
  private $scanStep = 0;
40
  }
41
  public function __construct(){
42
  $this->startTime = time();
43
+ $this->maxExecTime = self::getMaxExecutionTime();
44
  $this->i = new wfIssues();
45
  $this->i->deleteNew();
46
  $this->cycleStartTime = time();
92
  }
93
  }
94
  public function fork(){
95
+ wordfence::status(4, 'info', "Entered fork()");
96
  if(wfConfig::set_ser('wfsd_engine', $this, true)){
97
+ wordfence::status(4, 'info', "Calling startScan(true)");
98
  self::startScan(true);
99
  } //Otherwise there was an error so don't start another scan.
100
  exit(0);
861
  return "A scan is already running. Use the kill link if you would like to terminate the current scan.";
862
  }
863
  }
 
 
 
 
 
 
 
 
864
  $timeout = self::getMaxExecutionTime() - 2; //2 seconds shorter than max execution time which ensures that only 2 HTTP processes are ever occupied
865
+ $testURL = admin_url('admin-ajax.php') . '?action=wordfence_testAjax';
866
+ $testResult = wp_remote_post($testURL, array(
867
+ 'timeout' => $timeout,
868
+ 'blocking' => true,
869
  'sslverify' => false,
870
+ 'headers' => array()
871
+ ));
872
+
873
+ $cronKey = wfUtils::bigRandomHex();
874
+ wfConfig::set('currentCronKey', time() . ',' . $cronKey);
875
+ if( (! is_wp_error($testResult)) && is_array($testResult) && strstr($testResult['body'], 'WFSCANTESTOK') !== false){
876
+ //ajax requests can be sent by the server to itself
877
+ $cronURL = admin_url('admin-ajax.php');
878
+ $cronURL .= '?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&cronKey=' . $cronKey;
879
+ $headers = array();
880
+ wordfence::status(4, 'info', "Starting cron with normal ajax at URL $cronURL");
881
+ $result = wp_remote_post( $cronURL, array(
882
+ 'timeout' => $timeout, //Must be less than max execution time or more than 2 HTTP children will be occupied by scan
883
+ 'blocking' => true, //Non-blocking seems to block anyway, so we use blocking
884
+ 'sslverify' => false,
885
+ 'headers' => $headers
886
+ ) );
887
+ wordfence::status(4, 'info', "Scan process ended after forking.");
888
+ } else {
889
+ $cronURL = admin_url('admin-ajax.php');
890
+ $cronURL = preg_replace('/^(https?:\/\/)/i', '$1noc1.wordfence.com/scanp/', $cronURL);
891
+ $cronURL .= '?action=wordfence_doScan&isFork=' . ($isFork ? '1' : '0') . '&cronKey=' . $cronKey;
892
+ $headers = array();
893
+ wordfence::status(4, 'info', "Starting cron via proxy at URL $cronURL");
894
+ $result = wp_remote_post( $cronURL, array(
895
+ 'timeout' => $timeout, //Must be less than max execution time or more than 2 HTTP children will be occupied by scan
896
+ 'blocking' => true, //Non-blocking seems to block anyway, so we use blocking
897
+ 'sslverify' => false,
898
+ 'headers' => $headers
899
+ ) );
900
+ wordfence::status(4, 'info', "Scan process ended after forking.");
901
+ }
902
  return false; //No error
903
  }
904
  public function processResponse($result){
905
  return false;
906
  }
907
  public static function getMaxExecutionTime(){
908
+ $config = wfConfig::get('maxExecutionTime');
909
+ wordfence::status(4, 'info', "Got value from wf config maxExecutionTime: $config");
910
+ if(is_numeric($config) && $config >= 10){
911
+ wordfence::status(4, 'info', "getMaxExecutionTime() returning config value: $config");
912
+ return $config;
913
+ }
914
+ $ini = @ini_get('max_execution_time');
915
+ wordfence::status(4, 'info', "Got max_execution_time value from ini: $ini");
916
+ if(is_numeric($ini) && $ini >= 10){
917
+ $ini = floor($ini / 2);
918
+ wordfence::status(4, 'info', "getMaxExecutionTime() returning half ini value: $ini");
919
+ return $ini;
920
+ }
921
+ wordfence::status(4, 'info', "getMaxExecutionTime() returning default of: 15");
922
+ return 15;
923
  }
924
  }
925
 
lib/wfUtils.php CHANGED
@@ -197,11 +197,10 @@ class wfUtils {
197
  error_log("Caller for " . $caller['file'] . " line " . $caller['line'] . " is " . $c2['file'] . ' line ' . $c2['line']);
198
  }
199
  public static function getWPVersion(){
200
- global $wp_version;
201
- global $wordfence_wp_version;
202
- if(isset($wordfence_wp_version)){
203
- return $wordfence_wp_version;
204
  } else {
 
205
  return $wp_version;
206
  }
207
  }
@@ -305,7 +304,6 @@ class wfUtils {
305
  }
306
  $toResolve = array();
307
  $db = new wfDB();
308
- global $wp_version;
309
  global $wpdb;
310
  $locsTable = $wpdb->base_prefix . 'wfLocs';
311
  $IPLocs = array();
@@ -332,7 +330,7 @@ class wfUtils {
332
  }
333
  }
334
  if(sizeof($toResolve) > 0){
335
- $api = new wfAPI(wfConfig::get('apiKey'), $wp_version);
336
  try {
337
  $freshIPs = $api->call('resolve_ips', array(), array(
338
  'ips' => implode(',', $toResolve)
197
  error_log("Caller for " . $caller['file'] . " line " . $caller['line'] . " is " . $c2['file'] . ' line ' . $c2['line']);
198
  }
199
  public static function getWPVersion(){
200
+ if(wordfence::$wordfence_wp_version){
201
+ return wordfence::$wordfence_wp_version;
 
 
202
  } else {
203
+ global $wp_version;
204
  return $wp_version;
205
  }
206
  }
304
  }
305
  $toResolve = array();
306
  $db = new wfDB();
 
307
  global $wpdb;
308
  $locsTable = $wpdb->base_prefix . 'wfLocs';
309
  $IPLocs = array();
330
  }
331
  }
332
  if(sizeof($toResolve) > 0){
333
+ $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
334
  try {
335
  $freshIPs = $api->call('resolve_ips', array(), array(
336
  'ips' => implode(',', $toResolve)
lib/wordfenceClass.php CHANGED
@@ -13,6 +13,7 @@ require_once('wfConfig.php');
13
  require_once('wfSchema.php');
14
  class wordfence {
15
  public static $printStatus = false;
 
16
  protected static $lastURLError = false;
17
  protected static $curlContent = "";
18
  protected static $curlDataWritten = 0;
@@ -246,6 +247,8 @@ class wordfence {
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');
@@ -283,11 +286,14 @@ class wordfence {
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
 
13
  require_once('wfSchema.php');
14
  class wordfence {
15
  public static $printStatus = false;
16
+ public static $wordfence_wp_version = false;
17
  protected static $lastURLError = false;
18
  protected static $curlContent = "";
19
  protected static $curlDataWritten = 0;
247
  add_action('wp_ajax_wordfence_logHuman', 'wordfence::ajax_logHuman_callback');
248
  add_action('wp_ajax_nopriv_wordfence_doScan', 'wordfence::ajax_doScan_callback');
249
  add_action('wp_ajax_wordfence_doScan', 'wordfence::ajax_doScan_callback');
250
+ add_action('wp_ajax_nopriv_wordfence_testAjax', 'wordfence::ajax_testAjax_callback');
251
+ add_action('wp_ajax_wordfence_testAjax', 'wordfence::ajax_testAjax_callback');
252
 
253
 
254
  add_action('wordfence_start_scheduled_scan', 'wordfence::wordfenceStartScheduledScan');
286
  }
287
  }
288
  }
289
+ public static function ajax_testAjax_callback(){
290
+ die("WFSCANTESTOK");
291
+ }
292
  public static function ajax_doScan_callback(){
293
  ignore_user_abort(true);
294
+ self::$wordfence_wp_version = false;
295
  require(ABSPATH . 'wp-includes/version.php');
296
+ self::$wordfence_wp_version = $wp_version;
297
  require('wfScan.php');
298
  wfScan::wfScanMain();
299
 
lib/wordfenceHash.php CHANGED
@@ -192,6 +192,7 @@ class wordfenceHash {
192
  $file = substr($realFile, $this->striplen);
193
  if( (! $this->stoppedOnFile) && microtime(true) - $this->startTime > $this->engine->maxExecTime){ //max X seconds but don't allow fork if we're looking for the file we stopped on. Search mode is VERY fast.
194
  $this->stoppedOnFile = $file;
 
195
  $this->engine->fork();
196
  //exits
197
  }
192
  $file = substr($realFile, $this->striplen);
193
  if( (! $this->stoppedOnFile) && microtime(true) - $this->startTime > $this->engine->maxExecTime){ //max X seconds but don't allow fork if we're looking for the file we stopped on. Search mode is VERY fast.
194
  $this->stoppedOnFile = $file;
195
+ wordfence::status(4, 'info', "Calling fork() from wordfenceHash::processFile with maxExecTime: " . $this->engine->maxExecTime);
196
  $this->engine->fork();
197
  //exits
198
  }
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.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,6 +154,13 @@ or a theme, because often these have been updated to fix a security hole.
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.
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.4
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.4 =
158
+ * Fixed issue that caused scans to not complete.
159
+ * Fixed issue that caused scans to launch a large number of child processes due to very short scan timeout.
160
+ * Fixed issue that caused websites that don't know their own hostname to not be able to scan.
161
+ * Added workaround for a bug in Better WP Security breaking Wordfence due to their code overwriting the WP version.
162
+ * Optimized the way we calculate max execution time for each process while scanning.
163
+
164
  = 3.4.1 =
165
  * Removed wfscan.php script and now using pseudo-ajax calls to fire off scans. Much more reliable.
166
  * Removed visitor.php script and now using pseudo-ajax calls to log human visits.
wordfence.php CHANGED
@@ -4,10 +4,10 @@ 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.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
  }
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.4
8
  Author URI: http://wordfence.com/
9
  */
10
+ define('WORDFENCE_VERSION', '3.4.4');
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
  }