Wordfence Security – Firewall & Malware Scan - Version 6.2.7

Version Description

  • Improvement: WordPress 4.7 improvements for the Web Application Firewall.
  • Improvement: Updated signatures for hash-based malware detection.
  • Improvement: Automatically attempt to detect when a site is behind a proxy and has IP information in a different field.
  • Improvement: Added additional contextual help links.
  • Improvement: Significant performance improvement for determining the connecting IP.
  • Improvement: Better messaging for two-factor recovery codes.
  • Fix: Adjusted message when trying to block an IP in the whitelist.
  • Fix: Error log download links now work on Windows servers.
  • Fix: Avoid running out of memory when viewing very large activity logs.
  • Fix: Fixed warning that could be logged when following an unlock email link.
  • Fix: Tour popups on options page now scroll into view correctly.
Download this release

Release Info

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

Code changes from version 6.2.6 to 6.2.7

js/admin.js CHANGED
@@ -405,6 +405,10 @@
405
  jQuery('#pointer-close').after('<a id="pointer-primary" class="button-primary">' + buttonLabel + '</a>');
406
  jQuery('#pointer-primary').click(buttonCallback);
407
  }
 
 
 
 
408
  },
409
  startTourAgain: function() {
410
  var self = this;
@@ -1144,6 +1148,25 @@
1144
  });
1145
  }
1146
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1147
  fixFPD: function(issueID) {
1148
  var self = this;
1149
  var title = "Full Path Disclosure";
@@ -2237,9 +2260,10 @@
2237
 
2238
  var message = "Scan the code below with your authenticator app to add this account. Some authenticator apps also allow you to type in the text version instead.<br><div id=\"wfTwoFactorQRCodeTable\"></div><br><strong>Key:</strong> <input type=\"text\" size=\"45\" value=\"" + res.base32Secret + "\" onclick=\"this.select();\" readonly>";
2239
  if (res.recoveryCodes.length > 0) {
2240
- message = message + "<br><br><strong>Recovery Codes</strong><br><p>Use these codes to log in if you lose access to your authenticator device. Each one may be used only once.</p><ul id=\"wfTwoFactorRecoveryCodes\">";
2241
 
2242
  var recoveryCodeFileContents = "Cellphone Sign-In Recovery Codes - " + res.homeurl + " (" + res.username + ")\r\n";
 
2243
  var splitter = /.{4}/g;
2244
  for (var i = 0; i < res.recoveryCodes.length; i++) {
2245
  var code = res.recoveryCodes[i];
@@ -2268,9 +2292,10 @@
2268
  self.twoFacStatus('User added! Check the user\'s phone to get the activation code.');
2269
 
2270
  if (res.recoveryCodes.length > 0) {
2271
- var message = "<p>Use these codes to log in if you are unable to access your phone. Each one may be used only once.</p><ul id=\"wfTwoFactorRecoveryCodes\">";
2272
 
2273
  var recoveryCodeFileContents = "Cellphone Sign-In Recovery Codes - " + res.homeurl + " (" + res.username + ")\r\n";
 
2274
  var splitter = /.{4}/g;
2275
  for (var i = 0; i < res.recoveryCodes.length; i++) {
2276
  var code = res.recoveryCodes[i];
405
  jQuery('#pointer-close').after('<a id="pointer-primary" class="button-primary">' + buttonLabel + '</a>');
406
  jQuery('#pointer-primary').click(buttonCallback);
407
  }
408
+
409
+ $('html, body').animate({
410
+ scrollTop: $('.wp-pointer').offset().top - 100
411
+ }, 1000);
412
  },
413
  startTourAgain: function() {
414
  var self = this;
1148
  });
1149
  }
1150
  },
1151
+ useRecommendedHowGetIPs: function(issueID) {
1152
+ var self = this;
1153
+ this.ajax('wordfence_misconfiguredHowGetIPsChoice', {
1154
+ issueID: issueID,
1155
+ choice: 'yes'
1156
+ }, function(res) {
1157
+ if (res.ok) {
1158
+ jQuery('#wordfenceMisconfiguredHowGetIPsNotice').fadeOut();
1159
+
1160
+ self.loadIssues(function() {
1161
+ self.colorbox('400px', "Success updating option", "The 'How does Wordfence get IPs' option was successfully updated to the recommended value.");
1162
+ });
1163
+ } else if (res.cerrorMsg) {
1164
+ self.loadIssues(function() {
1165
+ self.colorbox('400px', 'An error occurred', res.cerrorMsg);
1166
+ });
1167
+ }
1168
+ });
1169
+ },
1170
  fixFPD: function(issueID) {
1171
  var self = this;
1172
  var title = "Full Path Disclosure";
2260
 
2261
  var message = "Scan the code below with your authenticator app to add this account. Some authenticator apps also allow you to type in the text version instead.<br><div id=\"wfTwoFactorQRCodeTable\"></div><br><strong>Key:</strong> <input type=\"text\" size=\"45\" value=\"" + res.base32Secret + "\" onclick=\"this.select();\" readonly>";
2262
  if (res.recoveryCodes.length > 0) {
2263
+ message = message + "<br><br><strong>Recovery Codes</strong><br><p>Use one of these " + res.recoveryCodes.length + " codes to log in if you lose access to your authenticator device. Codes are 16 characters long, plus optional spaces. Each one may be used only once.</p><ul id=\"wfTwoFactorRecoveryCodes\">";
2264
 
2265
  var recoveryCodeFileContents = "Cellphone Sign-In Recovery Codes - " + res.homeurl + " (" + res.username + ")\r\n";
2266
+ recoveryCodeFileContents = recoveryCodeFileContents + "\r\nEach line of 16 letters and numbers is a single recovery code, with optional spaces for readability. When typing your password, enter \"wf\" followed by the entire code like \"mypassword wf1234 5678 90AB CDEF\". If your site shows a separate prompt for entering a code after entering only your username and password, enter only the code like \"1234 5678 90AB CDEF\". Your recovery codes are:\r\n\r\n";
2267
  var splitter = /.{4}/g;
2268
  for (var i = 0; i < res.recoveryCodes.length; i++) {
2269
  var code = res.recoveryCodes[i];
2292
  self.twoFacStatus('User added! Check the user\'s phone to get the activation code.');
2293
 
2294
  if (res.recoveryCodes.length > 0) {
2295
+ var message = "<p>Use one of these " + res.recoveryCodes.length + " codes to log in if you are unable to access your phone. Codes are 16 characters long, plus optional spaces. Each one may be used only once.</p><ul id=\"wfTwoFactorRecoveryCodes\">";
2296
 
2297
  var recoveryCodeFileContents = "Cellphone Sign-In Recovery Codes - " + res.homeurl + " (" + res.username + ")\r\n";
2298
+ recoveryCodeFileContents = recoveryCodeFileContents + "\r\nEach line of 16 letters and numbers is a single recovery code, with optional spaces for readability. When typing your password, enter \"wf\" followed by the entire code like \"mypassword wf1234 5678 90AB CDEF\". If your site shows a separate prompt for entering a code after entering only your username and password, enter only the code like \"1234 5678 90AB CDEF\". Your recovery codes are:\r\n\r\n";
2299
  var splitter = /.{4}/g;
2300
  for (var i = 0; i < res.recoveryCodes.length; i++) {
2301
  var code = res.recoveryCodes[i];
js/tourTip.js CHANGED
@@ -49,6 +49,14 @@ window['wordfenceExt'] = {
49
  function(){ jQuery('#wordfenceFalconDeprecationWarning').fadeOut(); }
50
  );
51
  },
 
 
 
 
 
 
 
 
52
  removeFromCache: function(postID){
53
  this.ajax('wordfence_removeFromCache', {
54
  id: postID
49
  function(){ jQuery('#wordfenceFalconDeprecationWarning').fadeOut(); }
50
  );
51
  },
52
+ misconfiguredHowGetIPsChoice : function(choice) {
53
+ this.ajax('wordfence_misconfiguredHowGetIPsChoice', {
54
+ choice: choice
55
+ },
56
+ function(res){ jQuery('#wordfenceMisconfiguredHowGetIPsNotice').fadeOut(); },
57
+ function(){ jQuery('#wordfenceMisconfiguredHowGetIPsNotice').fadeOut(); }
58
+ );
59
+ },
60
  removeFromCache: function(postID){
61
  this.ajax('wordfence_removeFromCache', {
62
  id: postID
lib/menu_options.php CHANGED
@@ -439,6 +439,14 @@ $w = new wfConfig();
439
  name="scansEnabled_public" value="1" DISABLED /></td>
440
  </tr>
441
  <?php } ?>
 
 
 
 
 
 
 
 
442
  <tr>
443
  <th>Scan for the HeartBleed vulnerability<a
444
  href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_the_HeartBleed_vulnerability"
@@ -625,7 +633,9 @@ $w = new wfConfig();
625
  </td>
626
  </tr>
627
  <tr>
628
- <th>Limit the number of issues sent in the scan results email.</th>
 
 
629
  <td>
630
  <input type="text" name="scan_maxIssues" id="scan_maxIssues"
631
  value="<?php $w->f( 'scan_maxIssues' ); ?>"/> 0 or empty means unlimited
439
  name="scansEnabled_public" value="1" DISABLED /></td>
440
  </tr>
441
  <?php } ?>
442
+ <tr>
443
+ <th>Scan for misconfigured How does Wordfence get IPs<a
444
+ href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_misconfigured_How_does_Wordfence_get_IPs"
445
+ target="_blank" class="wfhelp"></a></th>
446
+ <td><input type="checkbox" id="scansEnabled_checkHowGetIPs" class="wfConfigElem"
447
+ name="scansEnabled_checkHowGetIPs" value="1" <?php $w->cb( 'scansEnabled_checkHowGetIPs' ); ?> />
448
+ </td>
449
+ </tr>
450
  <tr>
451
  <th>Scan for the HeartBleed vulnerability<a
452
  href="http://docs.wordfence.com/en/Wordfence_options#Scan_for_the_HeartBleed_vulnerability"
633
  </td>
634
  </tr>
635
  <tr>
636
+ <th>Limit the number of issues sent in the scan results email.<a
637
+ href="https://docs.wordfence.com/en/Wordfence_options#Limit_the_number_of_issues_sent_in_the_scan_results_email"
638
+ target="_blank" class="wfhelp"></a></th>
639
  <td>
640
  <input type="text" name="scan_maxIssues" id="scan_maxIssues"
641
  value="<?php $w->f( 'scan_maxIssues' ); ?>"/> 0 or empty means unlimited
lib/menu_scan.php CHANGED
@@ -1069,6 +1069,40 @@ $sigUpdateTime = wfConfig::get('signatureUpdateTime');
1069
  </div>
1070
  </div>
1071
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1072
 
1073
  <script type="text/x-jquery-template" id="issueTmpl_spamvertizeCheck">
1074
  <div>
1069
  </div>
1070
  </div>
1071
  </script>
1072
+ <script type="text/x-jquery-template" id="issueTmpl_checkHowGetIPs">
1073
+ <div>
1074
+ <div class="wfIssue">
1075
+ <h2>${shortMsg}</h2>
1076
+ <p>
1077
+ <table border="0" class="wfIssue" cellspacing="0" cellpadding="0">
1078
+ <tr><th>Issue first detected:</th><td>${timeAgo} ago.</td></tr>
1079
+ <tr><th>Severity:</th><td>{{if severity == '1'}}Critical{{else}}Warning{{/if}}</td></tr>
1080
+ <tr><th>Status</th><td>
1081
+ {{if status == 'new' }}New{{/if}}
1082
+ {{if status == 'ignoreC' }}This issue will be ignored until it changes.{{/if}}
1083
+ {{if status == 'ignoreP' }}This issue is permanently ignored.{{/if}}
1084
+ </td></tr>
1085
+ </table>
1086
+ </p>
1087
+ <p>
1088
+ {{html longMsg}}
1089
+ </p>
1090
+ <div class="wfIssueOptions">
1091
+ {{if status == 'new'}}
1092
+ <strong>Resolve:</strong>
1093
+ {{if ((typeof data.recommendation !== 'undefined') && data.recommendation)}}
1094
+ <a href="#" onclick="WFAD.useRecommendedHowGetIPs('${id}'); return false;">Use recommended value</a>
1095
+ {{/if}}
1096
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">I have fixed this issue</a>
1097
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'ignoreP'); return false;">Ignore this problem</a>
1098
+ {{/if}}
1099
+ {{if status == 'ignoreP' || status == 'ignoreC'}}
1100
+ <a href="#" onclick="WFAD.updateIssueStatus('${id}', 'delete'); return false;">Stop ignoring this issue</a>
1101
+ {{/if}}
1102
+ </div>
1103
+ </div>
1104
+ </div>
1105
+ </script>
1106
 
1107
  <script type="text/x-jquery-template" id="issueTmpl_spamvertizeCheck">
1108
  <div>
lib/menu_waf.php CHANGED
@@ -265,7 +265,9 @@ $wafRemoveURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=removeA
265
 
266
  <p id="whitelist-monitor">
267
  <strong>Monitor Background Requests for False Positives:</strong><br>
268
- <label><input type="checkbox" id="monitor-front" name="monitor-front" value="1"<?php echo wfConfig::get('ajaxWatcherDisabled_front') ? '' : ' checked'; ?>>Front</label> &nbsp; <label><input type="checkbox" id="monitor-admin" name="monitor-admin" value="1"<?php echo wfConfig::get('ajaxWatcherDisabled_admin') ? '' : ' checked'; ?>>Admin Panel</label>
 
 
269
  </p>
270
  <br>
271
 
@@ -273,7 +275,9 @@ $wafRemoveURL = network_admin_url('admin.php?page=WordfenceWAF&wafAction=removeA
273
 
274
  <p id="waf-advanced-options">
275
  <strong>Other Options</strong><br>
276
- <label><input type="checkbox" id="waf-disable-ip-blocking" name="waf-disable-ip-blocking" value="1"<?php echo $config->getConfig('disableWAFIPBlocking') ? ' checked' : ''; ?>>Delay IP and Country blocking until after WordPress and plugins have loaded (only process firewall rules early)</label>
 
 
277
  </p>
278
 
279
  <?php if (WFWAF_AUTO_PREPEND) : ?>
265
 
266
  <p id="whitelist-monitor">
267
  <strong>Monitor Background Requests for False Positives:</strong><br>
268
+ <label><input type="checkbox" id="monitor-front" name="monitor-front" value="1"<?php echo wfConfig::get('ajaxWatcherDisabled_front') ? '' : ' checked'; ?>>Front</label> &nbsp; <label><input type="checkbox" id="monitor-admin" name="monitor-admin" value="1"<?php echo wfConfig::get('ajaxWatcherDisabled_admin') ? '' : ' checked'; ?>>Admin Panel</label> <a
269
+ href="https://docs.wordfence.com/en/WAF#Whitelisted_URLs"
270
+ target="_blank" class="wfhelp"></a>
271
  </p>
272
  <br>
273
 
275
 
276
  <p id="waf-advanced-options">
277
  <strong>Other Options</strong><br>
278
+ <label><input type="checkbox" id="waf-disable-ip-blocking" name="waf-disable-ip-blocking" value="1"<?php echo $config->getConfig('disableWAFIPBlocking') ? ' checked' : ''; ?>>Delay IP and Country blocking until after WordPress and plugins have loaded (only process firewall rules early)</label> <a
279
+ href="https://docs.wordfence.com/en/WAF#Advanced_Configuration"
280
+ target="_blank" class="wfhelp"></a>
281
  </p>
282
 
283
  <?php if (WFWAF_AUTO_PREPEND) : ?>
lib/viewFullActivityLog.php CHANGED
@@ -13,12 +13,17 @@ $db = new wfDB();
13
  global $wpdb;
14
  $debugOn = wfConfig::get('debugOn', 0);
15
  $table = $wpdb->base_prefix . 'wfStatus';
16
- $q = $db->querySelect("select ctime, level, type, msg from $table order by ctime desc");
17
  $timeOffset = 3600 * get_option('gmt_offset');
18
- foreach($q as $r){
19
- if($r['level'] < 4 || $debugOn){
20
- echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime'] + $timeOffset) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . esc_html($r['msg']) . "</div>\n";
 
 
 
21
  }
 
 
22
  }
23
  ?>
24
  </body>
13
  global $wpdb;
14
  $debugOn = wfConfig::get('debugOn', 0);
15
  $table = $wpdb->base_prefix . 'wfStatus';
16
+ $offset = 0;
17
  $timeOffset = 3600 * get_option('gmt_offset');
18
+ $q = $db->querySelect("SELECT ctime, level, type, msg FROM {$table} ORDER BY ctime DESC LIMIT %d, 100", $offset);
19
+ while (is_array($q) && count($q) > 0) {
20
+ foreach($q as $r){
21
+ if($r['level'] < 4 || $debugOn){
22
+ echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime'] + $timeOffset) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . esc_html($r['msg']) . "</div>\n";
23
+ }
24
  }
25
+ $offset += count($q);
26
+ $q = $db->querySelect("SELECT ctime, level, type, msg FROM {$table} ORDER BY ctime DESC LIMIT %d, 100", $offset);
27
  }
28
  ?>
29
  </body>
lib/wfConfig.php CHANGED
@@ -36,6 +36,7 @@ class wfConfig {
36
  "lowResourceScansEnabled" => array('value' => false, 'autoload' => self::AUTOLOAD),
37
  "scansEnabled_public" => array('value' => false, 'autoload' => self::AUTOLOAD),
38
  "scansEnabled_heartbleed" => array('value' => true, 'autoload' => self::AUTOLOAD),
 
39
  "scansEnabled_core" => array('value' => true, 'autoload' => self::AUTOLOAD),
40
  "scansEnabled_themes" => array('value' => false, 'autoload' => self::AUTOLOAD),
41
  "scansEnabled_plugins" => array('value' => false, 'autoload' => self::AUTOLOAD),
36
  "lowResourceScansEnabled" => array('value' => false, 'autoload' => self::AUTOLOAD),
37
  "scansEnabled_public" => array('value' => false, 'autoload' => self::AUTOLOAD),
38
  "scansEnabled_heartbleed" => array('value' => true, 'autoload' => self::AUTOLOAD),
39
+ "scansEnabled_checkHowGetIPs" => array('value' => true, 'autoload' => self::AUTOLOAD),
40
  "scansEnabled_core" => array('value' => true, 'autoload' => self::AUTOLOAD),
41
  "scansEnabled_themes" => array('value' => false, 'autoload' => self::AUTOLOAD),
42
  "scansEnabled_plugins" => array('value' => false, 'autoload' => self::AUTOLOAD),
lib/wfLog.php CHANGED
@@ -285,15 +285,23 @@ class wfLog {
285
 
286
  /**
287
  * @param string $IP Should be in dot or colon notation (127.0.0.1 or ::1)
 
288
  * @return bool
289
  */
290
- public function isWhitelisted($IP) {
 
 
 
 
291
  foreach (wfUtils::getIPWhitelist() as $subnet) {
292
  if ($subnet instanceof wfUserIPRange) {
293
  if ($subnet->isIPInRange($IP)) {
294
  return true;
295
  }
296
  } elseif (wfUtils::subnetContainsIP($subnet, $IP)) {
 
 
 
297
  return true;
298
  }
299
  }
285
 
286
  /**
287
  * @param string $IP Should be in dot or colon notation (127.0.0.1 or ::1)
288
+ * @param bool $forcedWhitelistEntry If provided, returns whether or not the IP is on a forced whitelist.
289
  * @return bool
290
  */
291
+ public function isWhitelisted($IP, &$forcedWhitelistEntry = null) {
292
+ if ($forcedWhitelistEntry !== null) {
293
+ $forcedWhitelistEntry = false;
294
+ }
295
+
296
  foreach (wfUtils::getIPWhitelist() as $subnet) {
297
  if ($subnet instanceof wfUserIPRange) {
298
  if ($subnet->isIPInRange($IP)) {
299
  return true;
300
  }
301
  } elseif (wfUtils::subnetContainsIP($subnet, $IP)) {
302
+ if ($forcedWhitelistEntry !== null) {
303
+ $forcedWhitelistEntry = true;
304
+ }
305
  return true;
306
  }
307
  }
lib/wfScanEngine.php CHANGED
@@ -49,6 +49,8 @@ class wfScanEngine {
49
  private $knownFilesLoader;
50
 
51
  private $metrics = array();
 
 
52
 
53
  public static function testForFullPathDisclosure($url = null, $filePath = null) {
54
  if ($url === null && $filePath === null) {
@@ -73,7 +75,7 @@ class wfScanEngine {
73
  }
74
 
75
  public function __sleep(){ //Same order here as above for properties that are included in serialization
76
- return array('hasher', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues', 'suspectedFiles', 'dbScanner', 'knownFilesLoader', 'metrics');
77
  }
78
  public function __construct(){
79
  $this->startTime = time();
@@ -91,6 +93,8 @@ class wfScanEngine {
91
  $this->jobList[] = 'checkSpamIP';
92
  $this->jobList[] = 'checkGSB';
93
  $this->jobList[] = 'heartbleed';
 
 
94
  $this->jobList[] = 'knownFiles_init';
95
  $this->jobList[] = 'knownFiles_main';
96
  $this->jobList[] = 'knownFiles_finish';
@@ -340,6 +344,57 @@ class wfScanEngine {
340
  sleep(2);
341
  }
342
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
  private function scan_checkReadableConfig() {
345
  $haveIssues = false;
49
  private $knownFilesLoader;
50
 
51
  private $metrics = array();
52
+
53
+ private $checkHowGetIPsRequestTime = 0;
54
 
55
  public static function testForFullPathDisclosure($url = null, $filePath = null) {
56
  if ($url === null && $filePath === null) {
75
  }
76
 
77
  public function __sleep(){ //Same order here as above for properties that are included in serialization
78
+ return array('hasher', 'jobList', 'i', 'wp_version', 'apiKey', 'startTime', 'maxExecTime', 'publicScanEnabled', 'fileContentsResults', 'scanner', 'scanQueue', 'hoover', 'scanData', 'statusIDX', 'userPasswdQueue', 'passwdHasIssues', 'suspectedFiles', 'dbScanner', 'knownFilesLoader', 'metrics', 'checkHowGetIPsRequestTime');
79
  }
80
  public function __construct(){
81
  $this->startTime = time();
93
  $this->jobList[] = 'checkSpamIP';
94
  $this->jobList[] = 'checkGSB';
95
  $this->jobList[] = 'heartbleed';
96
+ $this->jobList[] = 'checkHowGetIPs_init';
97
+ $this->jobList[] = 'checkHowGetIPs_main';
98
  $this->jobList[] = 'knownFiles_init';
99
  $this->jobList[] = 'knownFiles_main';
100
  $this->jobList[] = 'knownFiles_finish';
344
  sleep(2);
345
  }
346
  }
347
+
348
+ private function scan_checkHowGetIPs_init() {
349
+ if (wfConfig::get('scansEnabled_checkHowGetIPs')) {
350
+ $this->statusIDX['checkHowGetIPs'] = wordfence::statusStart("Checking for the most secure way to get IPs");
351
+ $this->checkHowGetIPsRequestTime = time();
352
+ wfUtils::requestDetectProxyCallback();
353
+ }
354
+ else {
355
+ wordfence::statusDisabled("Skipping scan for misconfigured How does Wordfence get IPs");
356
+ }
357
+ }
358
+
359
+ private function scan_checkHowGetIPs_main() {
360
+ if (!defined('WORDFENCE_CHECKHOWGETIPS_TIMEOUT')) { define('WORDFENCE_CHECKHOWGETIPS_TIMEOUT', 30); }
361
+
362
+ if (wfConfig::get('scansEnabled_checkHowGetIPs')) {
363
+ $haveIssues = false;
364
+ $existing = wfConfig::get('howGetIPs', '');
365
+ $recommendation = wfConfig::get('detectProxyRecommendation', '');
366
+ while (empty($recommendation) && (time() - $this->checkHowGetIPsRequestTime) < WORDFENCE_CHECKHOWGETIPS_TIMEOUT) {
367
+ sleep(1);
368
+ $this->forkIfNeeded();
369
+ $recommendation = wfConfig::get('detectProxyRecommendation', '');
370
+ }
371
+
372
+ if ($recommendation == 'UNKNOWN' || empty($recommendation)) {
373
+ $this->addIssue('checkHowGetIPs', 2, 'checkHowGetIPs', 'checkHowGetIPs' . $recommendation . WORDFENCE_VERSION, "Unable to accurately detect IPs", 'Wordfence was unable to validate a test request to your website. This can happen if your website is behind a proxy that does not use one of the standard ways to convey the IP of the request or it is unreachable publicly. IP blocking and live traffic information may not be accurate. <a href="https://docs.wordfence.com/en/Misconfigured_how_get_IPs_notice " target="_blank">Get More Information</a>', array());
374
+ $haveIssues = true;
375
+ }
376
+ else if (!empty($existing) && $existing != $recommendation) {
377
+ $extraMsg = '';
378
+ if ($recommendation == 'REMOTE_ADDR') {
379
+ $extraMsg = ' For maximum security use PHP\'s built in REMOTE_ADDR.';
380
+ }
381
+ else if ($recommendation == 'HTTP_X_FORWARDED_FOR') {
382
+ $extraMsg = ' This site appears to be behind a front-end proxy, so using the X-Forwarded-For HTTP header will resolve to the correct IPs.';
383
+ }
384
+ else if ($recommendation == 'HTTP_X_REAL_IP') {
385
+ $extraMsg = ' This site appears to be behind a front-end proxy, so using the X-Real-IP HTTP header will resolve to the correct IPs.';
386
+ }
387
+ else if ($recommendation == 'HTTP_CF_CONNECTING_IP') {
388
+ $extraMsg = ' This site appears to be behind Cloudflare, so using the Cloudflare "CF-Connecting-IP" HTTP header will resolve to the correct IPs.';
389
+ }
390
+
391
+ $this->addIssue('checkHowGetIPs', 2, 'checkHowGetIPs', 'checkHowGetIPs' . $recommendation . WORDFENCE_VERSION, "'How does Wordfence get IPs' is misconfigured", 'A test request to this website was detected on a different value for this setting. IP blocking and live traffic information may not be accurate. <a href="https://docs.wordfence.com/en/Misconfigured_how_get_IPs_notice " target="_blank">Get More Information</a>' . $extraMsg, array('recommendation' => $recommendation));
392
+ $haveIssues = true;
393
+ }
394
+
395
+ wordfence::statusEnd($this->statusIDX['checkHowGetIPs'], $haveIssues);
396
+ }
397
+ }
398
 
399
  private function scan_checkReadableConfig() {
400
  $haveIssues = false;
lib/wfUtils.php CHANGED
@@ -630,6 +630,10 @@ class wfUtils {
630
  }
631
  }
632
  public static function getIP(){
 
 
 
 
633
  //For debugging.
634
  //return '54.232.205.132';
635
  //return self::makeRandomIP();
@@ -638,6 +642,7 @@ class wfUtils {
638
  $ip = self::getIPAndServerVarible();
639
  if (is_array($ip)) {
640
  list($IP, $variable) = $ip;
 
641
  return $IP;
642
  }
643
  return false;
@@ -658,7 +663,15 @@ class wfUtils {
658
  return self::getCleanIPAndServerVar($ipsToCheck);
659
  }
660
  } else {
661
- $ipsToCheck = array($connectionIP);
 
 
 
 
 
 
 
 
662
  if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
663
  $ipsToCheck[] = array($_SERVER['HTTP_X_FORWARDED_FOR'], 'HTTP_X_FORWARDED_FOR');
664
  }
@@ -1402,6 +1415,94 @@ class wfUtils {
1402
  }
1403
  return $output;
1404
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1405
  }
1406
 
1407
  // GeoIP lib uses these as well
630
  }
631
  }
632
  public static function getIP(){
633
+ static $theIP = null;
634
+ if (isset($theIP)) {
635
+ return $theIP;
636
+ }
637
  //For debugging.
638
  //return '54.232.205.132';
639
  //return self::makeRandomIP();
642
  $ip = self::getIPAndServerVarible();
643
  if (is_array($ip)) {
644
  list($IP, $variable) = $ip;
645
+ $theIP = $IP;
646
  return $IP;
647
  }
648
  return false;
663
  return self::getCleanIPAndServerVar($ipsToCheck);
664
  }
665
  } else {
666
+ $ipsToCheck = array();
667
+
668
+ $recommendedField = wfConfig::get('detectProxyRecommendation', ''); //Prioritize the result from our proxy check if done
669
+ if (!empty($recommendedField) && $recommendedField != 'UNKNOWN') {
670
+ if (isset($_SERVER[$recommendedField])) {
671
+ $ipsToCheck[] = array($_SERVER[$recommendedField], $recommendedField);
672
+ }
673
+ }
674
+ $ipsToCheck[] = $connectionIP;
675
  if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
676
  $ipsToCheck[] = array($_SERVER['HTTP_X_FORWARDED_FOR'], 'HTTP_X_FORWARDED_FOR');
677
  }
1415
  }
1416
  return $output;
1417
  }
1418
+
1419
+ public static function requestDetectProxyCallback($timeout = 0.01, $blocking = false) {
1420
+ $nonce = bin2hex(wfWAFUtils::random_bytes(32));
1421
+ $callback = self::getSiteBaseURL() . '?_wfsf=detectProxy';
1422
+
1423
+ wfConfig::set('detectProxyNonce', $nonce, wfConfig::DONT_AUTOLOAD);
1424
+ wfConfig::set('detectProxyRecommendation', '', wfConfig::DONT_AUTOLOAD);
1425
+
1426
+ $payload = array(
1427
+ 'nonce' => $nonce,
1428
+ 'callback' => $callback,
1429
+ );
1430
+
1431
+ $siteurl = '';
1432
+ if (function_exists('get_bloginfo')) {
1433
+ if (is_multisite()) {
1434
+ $siteurl = network_home_url();
1435
+ $siteurl = rtrim($siteurl, '/'); //Because previously we used get_bloginfo and it returns http://example.com without a '/' char.
1436
+ } else {
1437
+ $siteurl = home_url();
1438
+ }
1439
+ }
1440
+
1441
+ wp_remote_post(WFWAF_API_URL_SEC . "?" . http_build_query(array(
1442
+ 'action' => 'detect_proxy',
1443
+ 'k' => wfConfig::get('apiKey'),
1444
+ 's' => $siteurl,
1445
+ 't' => microtime(true),
1446
+ ), null, '&'),
1447
+ array(
1448
+ 'body' => json_encode($payload),
1449
+ 'headers' => array(
1450
+ 'Content-Type' => 'application/json',
1451
+ ),
1452
+ 'timeout' => $timeout,
1453
+ 'blocking' => $blocking,
1454
+ ));
1455
+
1456
+ //Asynchronous so we don't care about a response at this point.
1457
+ }
1458
+
1459
+ /**
1460
+ * @return bool Returns false if the payload is invalid, true if it processed the callback (even if the IP wasn't found).
1461
+ */
1462
+ public static function processDetectProxyCallback() {
1463
+ $nonce = wfConfig::get('detectProxyNonce', '');
1464
+ $testNonce = (isset($_POST['nonce']) ? $_POST['nonce'] : '');
1465
+ if (empty($nonce) || empty($testNonce)) {
1466
+ return false;
1467
+ }
1468
+
1469
+ if (!hash_equals($nonce, $testNonce)) {
1470
+ return false;
1471
+ }
1472
+
1473
+ $ips = (isset($_POST['ips']) ? $_POST['ips'] : array());
1474
+ if (empty($ips)) {
1475
+ return false;
1476
+ }
1477
+
1478
+ $expandedIPs = array();
1479
+ foreach ($ips as $ip) {
1480
+ $expandedIPs[] = self::inet_pton($ip);
1481
+ }
1482
+
1483
+ $checks = array('HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR');
1484
+ foreach ($checks as $key) {
1485
+ if (!isset($_SERVER[$key])) {
1486
+ continue;
1487
+ }
1488
+
1489
+ $testIP = self::getCleanIPAndServerVar(array(array($_SERVER[$key], $key)));
1490
+ if ($testIP === false) {
1491
+ continue;
1492
+ }
1493
+
1494
+ $testIP = self::inet_pton($testIP[0]);
1495
+ if (in_array($testIP, $expandedIPs)) {
1496
+ wfConfig::set('detectProxyRecommendation', $key, wfConfig::DONT_AUTOLOAD);
1497
+ wfConfig::set('detectProxyNonce', '', wfConfig::DONT_AUTOLOAD);
1498
+ return true;
1499
+ }
1500
+ }
1501
+
1502
+ wfConfig::set('detectProxyRecommendation', 'UNKNOWN', wfConfig::DONT_AUTOLOAD);
1503
+ wfConfig::set('detectProxyNonce', '', wfConfig::DONT_AUTOLOAD);
1504
+ return true;
1505
+ }
1506
  }
1507
 
1508
  // GeoIP lib uses these as well
lib/wordfenceClass.php CHANGED
@@ -564,6 +564,9 @@ SQL
564
  if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
565
  wfWAFIPBlocksController::synchronizeConfigSettings();
566
  }
 
 
 
567
 
568
  //Must be the final line
569
  }
@@ -1135,6 +1138,17 @@ SQL
1135
  exit();
1136
  }
1137
  }
 
 
 
 
 
 
 
 
 
 
 
1138
 
1139
  // Sync the WAF data with the database.
1140
  if (!WFWAF_SUBDIRECTORY_INSTALL && $waf = wfWAF::getInstance()) {
@@ -2533,6 +2547,22 @@ SQL
2533
  wfConfig::set('falconDeprecationChoice', '1');
2534
  return array('ok' => 1);
2535
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2536
  public static function ajax_removeFromCache_callback(){
2537
  $id = $_POST['id'];
2538
  $link = get_permalink($id);
@@ -2731,7 +2761,7 @@ SQL
2731
  exit;
2732
  }
2733
 
2734
- wfErrorLogHandler::outputErrorLog($_GET['logfile']); //exits
2735
  }
2736
  public static function ajax_addCacheExclusion_callback(){
2737
  $ex = wfConfig::get('cacheExclusions', false);
@@ -3194,8 +3224,13 @@ HTACCESS;
3194
  if ($IP == wfUtils::getIP()) {
3195
  return array('err' => 1, 'errorMsg' => "You can't block your own IP address.");
3196
  }
3197
- if ($log->isWhitelisted($IP)) {
3198
- return array('err' => 1, 'errorMsg' => "The IP address " . wp_kses($IP, array()) . " is whitelisted and can't be blocked or it is in a range of internal IP addresses that Wordfence does not block. You can remove this IP from the whitelist on the Wordfence options page.");
 
 
 
 
 
3199
  }
3200
  if (wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers') { //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
3201
  if (wfCrawl::isVerifiedGoogleCrawler($IP)) {
@@ -4310,7 +4345,7 @@ HTML;
4310
  'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues',
4311
  'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP',
4312
  'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'downloadHtaccess', 'downloadLogFile', 'checkFalconHtaccess',
4313
- 'updateConfig', 'saveCacheConfig', 'removeFromCache', 'autoUpdateChoice', 'adminEmailChoice', 'suPHPWAFUpdateChoice', 'falconDeprecationChoice', 'saveCacheOptions', 'clearPageCache',
4314
  'getCacheStats', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed',
4315
  'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
4316
  'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion',
@@ -4476,6 +4511,43 @@ HTML;
4476
  echo '<div id="wordfenceFalconDeprecationWarning" class="fade error"><p><strong>Support for the Falcon and Basic cache will be removed.</strong> This site currently has the ' . $cacheName . ' cache enabled, and it is scheduled to be removed in an upcoming release. Please investigate other caching options and then <a href="' . $url . '">click here to visit the cache settings page</a> to manually disable the cache. It will be disabled automatically when support is removed.</p><p>
4477
  <a class="button button-small wf-dismiss-link" href="#" onclick="wordfenceExt.falconDeprecationChoice(\'no\'); return false;">Dismiss</a></p></div>';
4478
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4479
  public static function autoUpdateNotice(){
4480
  echo '<div id="wordfenceAutoUpdateChoice" class="fade error"><p><strong>Do you want Wordfence to stay up-to-date automatically?</strong>&nbsp;&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'yes\'); return false;">Yes, enable auto-update.</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'no\'); return false;">No thanks.</a></p></div>';
4481
  }
@@ -4508,6 +4580,14 @@ HTML;
4508
  add_action('admin_notices', 'wordfence::falconDeprecationWarning');
4509
  }
4510
  }
 
 
 
 
 
 
 
 
4511
  if(! $warningAdded){
4512
  if(wfConfig::get('tourClosed') == '1' && (! wfConfig::get('autoUpdate')) && (! wfConfig::get('autoUpdateChoice'))){
4513
  $warningAdded = true;
564
  if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
565
  wfWAFIPBlocksController::synchronizeConfigSettings();
566
  }
567
+
568
+ //Check the How does Wordfence get IPs setting
569
+ wfUtils::requestDetectProxyCallback();
570
 
571
  //Must be the final line
572
  }
1138
  exit();
1139
  }
1140
  }
1141
+ else if ($wfFunc == 'detectProxy') {
1142
+ wfUtils::doNotCache();
1143
+ if (wfUtils::processDetectProxyCallback()) {
1144
+ self::getLog()->getCurrentRequest()->action = 'scan:detectproxy'; //Exempt a valid callback from live traffic
1145
+ echo wfConfig::get('detectProxyRecommendation', '-');
1146
+ }
1147
+ else {
1148
+ echo '0';
1149
+ }
1150
+ exit();
1151
+ }
1152
 
1153
  // Sync the WAF data with the database.
1154
  if (!WFWAF_SUBDIRECTORY_INSTALL && $waf = wfWAF::getInstance()) {
2547
  wfConfig::set('falconDeprecationChoice', '1');
2548
  return array('ok' => 1);
2549
  }
2550
+ public static function ajax_misconfiguredHowGetIPsChoice_callback() {
2551
+ $choice = $_POST['choice'];
2552
+ if ($choice == 'yes') {
2553
+ wfConfig::set('howGetIPs', wfConfig::get('detectProxyRecommendation', ''));
2554
+
2555
+ if (isset($_POST['issueID'])) {
2556
+ $issueID = intval($_POST['issueID']);
2557
+ $wfIssues = new wfIssues();
2558
+ $wfIssues->updateIssue($issueID, 'delete');
2559
+ }
2560
+ }
2561
+ else {
2562
+ wfConfig::set('misconfiguredHowGetIPsChoice' . WORDFENCE_VERSION, '1');
2563
+ }
2564
+ return array('ok' => 1);
2565
+ }
2566
  public static function ajax_removeFromCache_callback(){
2567
  $id = $_POST['id'];
2568
  $link = get_permalink($id);
2761
  exit;
2762
  }
2763
 
2764
+ wfErrorLogHandler::outputErrorLog(stripslashes($_GET['logfile'])); //exits
2765
  }
2766
  public static function ajax_addCacheExclusion_callback(){
2767
  $ex = wfConfig::get('cacheExclusions', false);
3224
  if ($IP == wfUtils::getIP()) {
3225
  return array('err' => 1, 'errorMsg' => "You can't block your own IP address.");
3226
  }
3227
+ $forcedWhitelistEntry = false;
3228
+ if ($log->isWhitelisted($IP, $forcedWhitelistEntry)) {
3229
+ $message = "The IP address " . wp_kses($IP, array()) . " is whitelisted and can't be blocked. You can remove this IP from the whitelist on the Wordfence options page.";
3230
+ if ($forcedWhitelistEntry) {
3231
+ $message = "The IP address " . wp_kses($IP, array()) . " is in a range of internal IP addresses that Wordfence does not block.";
3232
+ }
3233
+ return array('err' => 1, 'errorMsg' => $message);
3234
  }
3235
  if (wfConfig::get('neverBlockBG') != 'treatAsOtherCrawlers') { //Either neverBlockVerified or neverBlockUA is selected which means the user doesn't want to block google
3236
  if (wfCrawl::isVerifiedGoogleCrawler($IP)) {
4345
  'activityLogUpdate', 'ticker', 'loadIssues', 'updateIssueStatus', 'deleteIssue', 'updateAllIssues',
4346
  'reverseLookup', 'unlockOutIP', 'loadBlockRanges', 'unblockRange', 'blockIPUARange', 'whois', 'unblockIP',
4347
  'blockIP', 'permBlockIP', 'loadStaticPanel', 'saveConfig', 'downloadHtaccess', 'downloadLogFile', 'checkFalconHtaccess',
4348
+ 'updateConfig', 'saveCacheConfig', 'removeFromCache', 'autoUpdateChoice', 'adminEmailChoice', 'suPHPWAFUpdateChoice', 'falconDeprecationChoice', 'misconfiguredHowGetIPsChoice', 'saveCacheOptions', 'clearPageCache',
4349
  'getCacheStats', 'clearAllBlocked', 'killScan', 'saveCountryBlocking', 'saveScanSchedule', 'tourClosed',
4350
  'welcomeClosed', 'startTourAgain', 'downgradeLicense', 'addTwoFactor', 'twoFacActivate', 'twoFacDel',
4351
  'loadTwoFactor', 'loadAvgSitePerf', 'sendTestEmail', 'addCacheExclusion', 'removeCacheExclusion',
4511
  echo '<div id="wordfenceFalconDeprecationWarning" class="fade error"><p><strong>Support for the Falcon and Basic cache will be removed.</strong> This site currently has the ' . $cacheName . ' cache enabled, and it is scheduled to be removed in an upcoming release. Please investigate other caching options and then <a href="' . $url . '">click here to visit the cache settings page</a> to manually disable the cache. It will be disabled automatically when support is removed.</p><p>
4512
  <a class="button button-small wf-dismiss-link" href="#" onclick="wordfenceExt.falconDeprecationChoice(\'no\'); return false;">Dismiss</a></p></div>';
4513
  }
4514
+ public static function misconfiguredHowGetIPsNotice() {
4515
+ $url = network_admin_url('admin.php?page=WordfenceSecOpt');
4516
+ $existing = wfConfig::get('howGetIPs', '');
4517
+ $recommendation = wfConfig::get('detectProxyRecommendation', '');
4518
+ if (empty($existing) || empty($recommendation) || $recommendation == 'UNKNOWN' || $existing == $recommendation) {
4519
+ return;
4520
+ }
4521
+ $existingMsg = '';
4522
+ if ($existing == 'REMOTE_ADDR') {
4523
+ $existingMsg = 'This site is currently using PHP\'s built in REMOTE_ADDR.';
4524
+ }
4525
+ else if ($existing == 'HTTP_X_FORWARDED_FOR') {
4526
+ $existingMsg = 'This site is currently using the X-Forwarded-For HTTP header, which should only be used when the site is behind a front-end proxy that outputs this header.';
4527
+ }
4528
+ else if ($existing == 'HTTP_X_REAL_IP') {
4529
+ $existingMsg = 'This site is currently using the X-Real-IP HTTP header, which should only be used when the site is behind a front-end proxy that outputs this header.';
4530
+ }
4531
+ else if ($existing == 'HTTP_CF_CONNECTING_IP') {
4532
+ $existingMsg = 'This site is currently using the Cloudflare "CF-Connecting-IP" HTTP header, which should only be used when the site is behind Cloudflare.';
4533
+ }
4534
+
4535
+ $recommendationMsg = '';
4536
+ if ($recommendation == 'REMOTE_ADDR') {
4537
+ $recommendationMsg = 'For maximum security use PHP\'s built in REMOTE_ADDR.';
4538
+ }
4539
+ else if ($recommendation == 'HTTP_X_FORWARDED_FOR') {
4540
+ $recommendationMsg = 'This site appears to be behind a front-end proxy, so using the X-Forwarded-For HTTP header will resolve to the correct IPs.';
4541
+ }
4542
+ else if ($recommendation == 'HTTP_X_REAL_IP') {
4543
+ $recommendationMsg = 'This site appears to be behind a front-end proxy, so using the X-Real-IP HTTP header will resolve to the correct IPs.';
4544
+ }
4545
+ else if ($recommendation == 'HTTP_CF_CONNECTING_IP') {
4546
+ $recommendationMsg = 'This site appears to be behind Cloudflare, so using the Cloudflare "CF-Connecting-IP" HTTP header will resolve to the correct IPs.';
4547
+ }
4548
+ echo '<div id="wordfenceMisconfiguredHowGetIPsNotice" class="fade error"><p><strong>Your \'How does Wordfence get IPs\' setting is misconfigured.</strong> ' . $existingMsg . ' ' . $recommendationMsg . ' <a href="#" onclick="wordfenceExt.misconfiguredHowGetIPsChoice(\'yes\'); return false;">Click here to use the recommended setting</a> or <a href="' . $url . '">visit the options page</a> to manually update it.</p><p>
4549
+ <a class="button button-small wf-dismiss-link" href="#" onclick="wordfenceExt.misconfiguredHowGetIPsChoice(\'no\'); return false;">Dismiss</a> <a class="wfhelp" target="_blank" href="https://docs.wordfence.com/en/Misconfigured_how_get_IPs_notice"></a></p></div>';
4550
+ }
4551
  public static function autoUpdateNotice(){
4552
  echo '<div id="wordfenceAutoUpdateChoice" class="fade error"><p><strong>Do you want Wordfence to stay up-to-date automatically?</strong>&nbsp;&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'yes\'); return false;">Yes, enable auto-update.</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="wordfenceExt.autoUpdateChoice(\'no\'); return false;">No thanks.</a></p></div>';
4553
  }
4580
  add_action('admin_notices', 'wordfence::falconDeprecationWarning');
4581
  }
4582
  }
4583
+ if (!wfConfig::get('misconfiguredHowGetIPsChoice' . WORDFENCE_VERSION) && !(defined('WORDFENCE_DISABLE_MISCONFIGURED_HOWGETIPS') && WORDFENCE_DISABLE_MISCONFIGURED_HOWGETIPS)) {
4584
+ $warningAdded = true;
4585
+ if(wfUtils::isAdminPageMU()){
4586
+ add_action('network_admin_notices', 'wordfence::misconfiguredHowGetIPsNotice');
4587
+ } else {
4588
+ add_action('admin_notices', 'wordfence::misconfiguredHowGetIPsNotice');
4589
+ }
4590
+ }
4591
  if(! $warningAdded){
4592
  if(wfConfig::get('tourClosed') == '1' && (! wfConfig::get('autoUpdate')) && (! wfConfig::get('autoUpdateChoice'))){
4593
  $warningAdded = true;
lib/wordfenceHash.php CHANGED
@@ -622,7 +622,7 @@ class wordfenceHash {
622
  }
623
 
624
  //Will be malware scanned, return true
625
- if (strpos($file, 'lib/wordfenceScanner.php') === false && ($fileExt == 'js')) {
626
  return true;
627
  }
628
 
622
  }
623
 
624
  //Will be malware scanned, return true
625
+ if ($fileExt == 'js') {
626
  return true;
627
  }
628
 
lib/wordfenceScanner.php CHANGED
@@ -300,8 +300,8 @@ class wordfenceScanner {
300
  break;
301
  }
302
  }
303
- else if(strpos($file, 'lib/wordfenceScanner.php') === false) {
304
- $regexMatched = false;
305
  foreach ($this->patterns['rules'] as $rule) {
306
  $type = (isset($rule[4]) && !empty($rule[4])) ? $rule[4] : 'server';
307
  $logOnly = (isset($rule[5]) && !empty($rule[5])) ? $rule[5] : false;
@@ -335,7 +335,7 @@ class wordfenceScanner {
335
  }
336
  }
337
  if ($regexMatched) { break; }
338
- }
339
  if ($treatAsBinary && wfConfig::get('scansEnabled_highSense')) {
340
  $badStringFound = false;
341
  if (strpos($data, $this->patterns['badstrings'][0]) !== false) {
300
  break;
301
  }
302
  }
303
+ else {
304
+ $regexMatched = false;
305
  foreach ($this->patterns['rules'] as $rule) {
306
  $type = (isset($rule[4]) && !empty($rule[4])) ? $rule[4] : 'server';
307
  $logOnly = (isset($rule[5]) && !empty($rule[5])) ? $rule[5] : false;
335
  }
336
  }
337
  if ($regexMatched) { break; }
338
+ }
339
  if ($treatAsBinary && wfConfig::get('scansEnabled_highSense')) {
340
  $badStringFound = false;
341
  if (strpos($data, $this->patterns['badstrings'][0]) !== false) {
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking
4
  Requires at least: 3.9
5
  Tested up to: 4.7.0
6
- Stable tag: 6.2.6
7
 
8
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
 
@@ -190,6 +190,19 @@ Secure your website with Wordfence.
190
 
191
  == Changelog ==
192
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  = 6.2.6 =
194
  * Improvement: Improved formatting of attack data when it contains binary characters.
195
  * Improvement: Updated internal GeoIP database.
3
  Tags: security, secure, security plugin, wordpress security, login security, firewall, malware, antivirus, web application firewall, block hackers, country blocking
4
  Requires at least: 3.9
5
  Tested up to: 4.7.0
6
+ Stable tag: 6.2.7
7
 
8
  Secure your website with the most comprehensive WordPress security plugin. Firewall, malware scan, blocking, live traffic, login security & more.
9
 
190
 
191
  == Changelog ==
192
 
193
+ = 6.2.7 =
194
+ * Improvement: WordPress 4.7 improvements for the Web Application Firewall.
195
+ * Improvement: Updated signatures for hash-based malware detection.
196
+ * Improvement: Automatically attempt to detect when a site is behind a proxy and has IP information in a different field.
197
+ * Improvement: Added additional contextual help links.
198
+ * Improvement: Significant performance improvement for determining the connecting IP.
199
+ * Improvement: Better messaging for two-factor recovery codes.
200
+ * Fix: Adjusted message when trying to block an IP in the whitelist.
201
+ * Fix: Error log download links now work on Windows servers.
202
+ * Fix: Avoid running out of memory when viewing very large activity logs.
203
+ * Fix: Fixed warning that could be logged when following an unlock email link.
204
+ * Fix: Tour popups on options page now scroll into view correctly.
205
+
206
  = 6.2.6 =
207
  * Improvement: Improved formatting of attack data when it contains binary characters.
208
  * Improvement: Updated internal GeoIP database.
waf/bootstrap.php CHANGED
@@ -29,6 +29,10 @@ class wfWAFWordPressRequest extends wfWAFRequest {
29
  }
30
 
31
  public function getIP() {
 
 
 
 
32
  $howGet = wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs');
33
  if (is_string($howGet) && is_array($_SERVER) && array_key_exists($howGet, $_SERVER)) {
34
  $ips[] = array($_SERVER[$howGet], $howGet);
@@ -37,8 +41,10 @@ class wfWAFWordPressRequest extends wfWAFRequest {
37
  $cleanedIP = $this->_getCleanIPAndServerVar($ips);
38
  if (is_array($cleanedIP)) {
39
  list($ip, $variable) = $cleanedIP;
 
40
  return $ip;
41
  }
 
42
  return $cleanedIP;
43
  }
44
 
29
  }
30
 
31
  public function getIP() {
32
+ static $theIP = null;
33
+ if (isset($theIP)) {
34
+ return $theIP;
35
+ }
36
  $howGet = wfWAF::getInstance()->getStorageEngine()->getConfig('howGetIPs');
37
  if (is_string($howGet) && is_array($_SERVER) && array_key_exists($howGet, $_SERVER)) {
38
  $ips[] = array($_SERVER[$howGet], $howGet);
41
  $cleanedIP = $this->_getCleanIPAndServerVar($ips);
42
  if (is_array($cleanedIP)) {
43
  list($ip, $variable) = $cleanedIP;
44
+ $theIP = $ip;
45
  return $ip;
46
  }
47
+ $theIP = $cleanedIP;
48
  return $cleanedIP;
49
  }
50
 
wordfence.php CHANGED
@@ -4,14 +4,14 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
- Version: 6.2.6
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
- define('WORDFENCE_VERSION', '6.2.6');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17
 
4
  Plugin URI: http://www.wordfence.com/
5
  Description: Wordfence Security - Anti-virus, Firewall and Malware Scan
6
  Author: Wordfence
7
+ Version: 6.2.7
8
  Author URI: http://www.wordfence.com/
9
  Network: true
10
  */
11
  if(defined('WP_INSTALLING') && WP_INSTALLING){
12
  return;
13
  }
14
+ define('WORDFENCE_VERSION', '6.2.7');
15
  define('WORDFENCE_BASENAME', function_exists('plugin_basename') ? plugin_basename(__FILE__) :
16
  basename(dirname(__FILE__)) . '/' . basename(__FILE__));
17