Wordfence Security – Firewall & Malware Scan - Version 2.1.5

Version Description

  • Fixed bug that caused WF to not work when certain DB caching plugins are used and override wpdb object.
  • Fixed Wordfence so activity log only shows our own errors unless in debug mode.
  • Wordfence now deletes all it's tables and deletes all saved options when you deactivate the plugin.
  • Removed all exit() on error statements. Critical errors are handled more gracefully by writing to the log instead.
  • Fixed a bug that would cause a database loop until running out of memory under certain error conditions.
  • Suppressed useless warnings that occur in environments with basedir set or where functions are disabled for security reasons.
  • Removed redundant check that executed on every request and put it in activation instead.
  • If serialization during scan breaks, exit gracefully instead of looping.
  • Disk space in log is now shown as Gigabytes and formatted nicely.
  • Removed wdie() function which is a little obnoxious. Writing to WF error log instead.
  • Fixed bug where a non-empty but useless HTTP header can break getIP() function.
  • Added useful data to error output if getIP() tells you it can't work on your system.
  • Removed option to start scan in debug because it's no longer possible with a forked scan.
  • Removed option to test process running time on a system because it breaks on most systems and confuses customers.
  • Database connection errors no longer call die() but log an error instead in a way that removes the risk of a logging loop.
  • Removed dropAll.php script because we now clean up tables on deactivate and it's not needed.
  • Updated readme to show that we support 3.4.
Download this release

Release Info

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

Code changes from version 2.1.4 to 2.1.5

lib/dropAll.php DELETED
@@ -1,8 +0,0 @@
1
- <?php
2
- require_once('wfSchema.php');
3
- if((! isset($_SERVER)) || isset($_SERVER['REQUEST_URI'])){ echo "Running under web interface. Exiting.\n"; exit(0); }
4
- if(! (isset($argv[1]) && isset($argv[2]) && isset($argv[3]) && isset($argv[4])) ){ echo "Usage: {$argv[0]} <DB username> <DB password> <DB name> <prefix>\n"; exit(); } $s = new wfSchema('localhost', $argv[1], $argv[2], $argv[3]);
5
-
6
- $s->dropAll($argv[4]);
7
-
8
- ?>
lib/menu_options.php CHANGED
@@ -208,9 +208,7 @@ var WFSLevels = <?php echo json_encode(wfConfig::$securityLevels); ?>;
208
<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>
209
<tr><th>Enable debugging mode</th><td><input type="checkbox" id="debugOn" class="wfConfigElem" name="debugOn" value="1" <?php $w->cb('debugOn'); ?> /></td></tr>
210
<tr><th colspan="2"><a href="/?_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>
211
- <tr><th colspan="2"><a href="<?php echo wfUtils::getBaseURL(); ?>wfscan.php?debugMode=1&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Start a scan in debug mode (advanced users only)</a></th></tr>
212
<tr><th colspan="2"><a href="/?_wfsf=testmem&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Test your WordPress host's available memory</a></th></tr>
213
- <tr><th colspan="2"><a href="/?_wfsf=testtime&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Test your WordPress host's process running time (you may see a blank screen for up to 3 minutes)</a></th></tr>
214
</table>
215
<p><table border="0" cellpadding="0" cellspacing="0"><tr><td><input type="button" id="button1" name="button1" class="button-primary" value="Save Changes" onclick="WFAD.saveConfig();" /></td><td style="height: 24px;"><div class="wfAjax24"></div><span class="wfSavedMsg">&nbsp;Your changes have been saved!</span></td></tr></table></p>
216
</div>
208
<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>
209
<tr><th>Enable debugging mode</th><td><input type="checkbox" id="debugOn" class="wfConfigElem" name="debugOn" value="1" <?php $w->cb('debugOn'); ?> /></td></tr>
210
<tr><th colspan="2"><a href="/?_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>
211
<tr><th colspan="2"><a href="/?_wfsf=testmem&nonce=<?php echo wp_create_nonce('wp-ajax'); ?>" target="_blank">Test your WordPress host's available memory</a></th></tr>
212
</table>
213
<p><table border="0" cellpadding="0" cellspacing="0"><tr><td><input type="button" id="button1" name="button1" class="button-primary" value="Save Changes" onclick="WFAD.saveConfig();" /></td><td style="height: 24px;"><div class="wfAjax24"></div><span class="wfSavedMsg">&nbsp;Your changes have been saved!</span></td></tr></table></p>
214
</div>
lib/viewFullActivityLog.php CHANGED
@@ -11,10 +11,13 @@
11
<?php
12
$db = new wfDB();
13
global $wpdb;
14
$table = $wpdb->base_prefix . 'wfStatus';
15
$q = $db->query("select ctime, level, type, msg from $table order by ctime desc");
16
while($r = mysql_fetch_assoc($q)){
17
- echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime']) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . htmlspecialchars($r['msg']) . "</div>\n";
18
}
19
?>
20
</body>
11
<?php
12
$db = new wfDB();
13
global $wpdb;
14
+ $debugOn = wfConfig::get('debugOn', 0);
15
$table = $wpdb->base_prefix . 'wfStatus';
16
$q = $db->query("select ctime, level, type, msg from $table order by ctime desc");
17
while($r = mysql_fetch_assoc($q)){
18
+ if($r['level'] < 4 || $debugOn){
19
+ echo '<div' . ($r['type'] == 'error' ? ' class="error"' : '') . '>[' . date('M d H:i:s', $r['ctime']) . ':' . $r['ctime'] . ':' . $r['level'] . ':' . $r['type'] . ']&nbsp;' . htmlspecialchars($r['msg']) . "</div>\n";
20
+ }
21
}
22
?>
23
</body>
lib/wfConfig.php CHANGED
@@ -376,7 +376,7 @@ class wfConfig {
376
$msg = "wfConfig::set() got an array as second param with key: $key - Please report this bug. Exiting.";
377
wfstatus(1, 'error', $msg);
378
error_log($msg);
379
- exit(0);
380
}
381
382
self::getDB()->query("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
@@ -450,7 +450,7 @@ class wfConfig {
450
wordfence::status(4, 'info', "Serialized data for $key is " . strlen($serialized) . " bytes and is greater than max_allowed packet so writing it to disk file: " . $fullFile);
451
} else {
452
wordfence::status(1, 'error', "Your database doesn't allow big packets so we have to use files to store temporary data and Wordfence can't find a place to write them. Either ask your admin to increase max_allowed_packet on your MySQL database, or make one of the following directories writable by your web server: " . implode(', ', $dirs));
453
- exit();
454
}
455
fwrite($fh, self::$tmpFileHeader);
456
fwrite($fh, $serialized);
@@ -458,12 +458,12 @@ class wfConfig {
458
return true;
459
} else {
460
wordfence::status(1, 'error', "Wordfence tried to save a variable with name '$key' and your database max_allowed_packet is set to be too small. We then tried to save it to disk, but you don't have a temporary directory that is writable. You can fix this by making the /wp-content/plugins/wordfence/tmp/ directory writable by your web server. Or by increasing your max_allowed_packet configuration variable in your mysql database.");
461
- exit(0);
462
}
463
464
} else {
465
wordfence::status(1, 'error', "Wordfence tried to save a variable with name '$key' and your database max_allowed_packet is set to be too small. This particular variable can't be saved to disk. Please ask your administrator to increase max_allowed_packet and also report this in the Wordfence forums because it may be a bug. Thanks.");
466
- exit(0);
467
}
468
} else {
469
//Delete temp files on disk or else the DB will be written to but get_ser will see files on disk and read them instead
@@ -496,17 +496,19 @@ class wfConfig {
496
if(! self::$tmpDirCache){
497
$dirs = array(wfUtils::getPluginBaseDir() . 'wordfence/tmp/', sys_get_temp_dir(), ABSPATH . 'wp-content/uploads/');
498
$finalDir = 'notmp';
499
foreach($dirs as $dir){
500
$dir = rtrim($dir, '/') . '/';
501
- $fh = fopen($dir . 'wftmptest.txt', 'w');
502
if(! $fh){ continue; }
503
- $bytes = fwrite($fh, 'test');
504
- if($bytes != 4){ fclose($fh); continue; }
505
- fclose($fh);
506
- if(! unlink($dir . 'wftmptest.txt')){ continue; }
507
$finalDir = $dir;
508
break;
509
}
510
self::$tmpDirCache = $finalDir;
511
}
512
if(self::$tmpDirCache == 'notmp'){
376
$msg = "wfConfig::set() got an array as second param with key: $key - Please report this bug. Exiting.";
377
wfstatus(1, 'error', $msg);
378
error_log($msg);
379
+ return;
380
}
381
382
self::getDB()->query("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
450
wordfence::status(4, 'info', "Serialized data for $key is " . strlen($serialized) . " bytes and is greater than max_allowed packet so writing it to disk file: " . $fullFile);
451
} else {
452
wordfence::status(1, 'error', "Your database doesn't allow big packets so we have to use files to store temporary data and Wordfence can't find a place to write them. Either ask your admin to increase max_allowed_packet on your MySQL database, or make one of the following directories writable by your web server: " . implode(', ', $dirs));
453
+ return false;
454
}
455
fwrite($fh, self::$tmpFileHeader);
456
fwrite($fh, $serialized);
458
return true;
459
} else {
460
wordfence::status(1, 'error', "Wordfence tried to save a variable with name '$key' and your database max_allowed_packet is set to be too small. We then tried to save it to disk, but you don't have a temporary directory that is writable. You can fix this by making the /wp-content/plugins/wordfence/tmp/ directory writable by your web server. Or by increasing your max_allowed_packet configuration variable in your mysql database.");
461
+ return false;
462
}
463
464
} else {
465
wordfence::status(1, 'error', "Wordfence tried to save a variable with name '$key' and your database max_allowed_packet is set to be too small. This particular variable can't be saved to disk. Please ask your administrator to increase max_allowed_packet and also report this in the Wordfence forums because it may be a bug. Thanks.");
466
+ return false;
467
}
468
} else {
469
//Delete temp files on disk or else the DB will be written to but get_ser will see files on disk and read them instead
496
if(! self::$tmpDirCache){
497
$dirs = array(wfUtils::getPluginBaseDir() . 'wordfence/tmp/', sys_get_temp_dir(), ABSPATH . 'wp-content/uploads/');
498
$finalDir = 'notmp';
499
+ wfUtils::errorsOff();
500
foreach($dirs as $dir){
501
$dir = rtrim($dir, '/') . '/';
502
+ $fh = @fopen($dir . 'wftmptest.txt', 'w');
503
if(! $fh){ continue; }
504
+ $bytes = @fwrite($fh, 'test');
505
+ if($bytes != 4){ @fclose($fh); continue; }
506
+ @fclose($fh);
507
+ if(! @unlink($dir . 'wftmptest.txt')){ continue; }
508
$finalDir = $dir;
509
break;
510
}
511
+ wfUtils::errorsOn();
512
self::$tmpDirCache = $finalDir;
513
}
514
if(self::$tmpDirCache == 'notmp'){
lib/wfDB.php CHANGED
@@ -16,21 +16,32 @@ class wfDB {
16
} else {
17
global $wpdb;
18
if(! $wpdb){
19
- self::wfdie("The WordPress variable wpdb is not defined.");
20
}
21
- if(! $wpdb->dbhost ){ self::wfdie("The WordPress variable from wpdb dbhost is not defined."); }
22
- if(! $wpdb->dbuser ){ self::wfdie("The WordPress variable from wpdb dbuser is not defined."); }
23
- if(! isset($wpdb->dbpassword) ){ self::wfdie("The WordPress variable from wpdb dbpassword is not defined."); }
24
- if(! $wpdb->dbname ){ self::wfdie("The WordPress variable from wpdb dbname is not defined."); }
25
- $this->dbhost = $wpdb->dbhost;
26
- $this->dbuser = $wpdb->dbuser;
27
- $this->dbpassword = $wpdb->dbpassword;
28
- $this->dbname = $wpdb->dbname;
29
}
30
if($createNewHandle){
31
$dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
32
if($dbh === false){
33
- self::wfdie("Could not connect to database on " . $this->dbhost . " with user " . $this->dbuser . ' : ' . mysql_error());
34
}
35
mysql_select_db($this->dbname, $dbh);
36
$this->dbh = $dbh;
@@ -45,7 +56,8 @@ class wfDB {
45
} else {
46
$dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
47
if($dbh === false){
48
- self::wfdie("Could not connect to database on " . $this->dbhost . " with user " . $this->dbuser . ' : ' . mysql_error());
49
}
50
51
mysql_select_db($this->dbname, $dbh);
@@ -66,16 +78,32 @@ class wfDB {
66
} else if(sizeof($args) > 1){
67
$query = call_user_func_array('sprintf', $args);
68
} else {
69
- wfdie("No arguments passed to querySingle()");
70
}
71
$res = mysql_query($query, $this->dbh);
72
- $err = mysql_error();
73
- if( (! preg_match('/Wordfence DB error/i', $query)) && $err){ //prevent loops
74
- $this->errorMsg = $err;
75
- $trace=debug_backtrace(); $caller=array_shift($trace); wordfence::status(2, 'error', "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
76
- }
77
return mysql_fetch_assoc($res); //returns false if no rows found
78
}
79
public function querySingle(){
80
$this->errorMsg = false;
81
$args = func_get_args();
@@ -87,14 +115,10 @@ class wfDB {
87
}
88
$query = call_user_func_array('sprintf', $args);
89
} else {
90
- wfdie("No arguments passed to querySingle()");
91
}
92
$res = mysql_query($query, $this->dbh);
93
- $err = mysql_error();
94
- if( (! preg_match('/Wordfence DB error/i', $query)) && $err){
95
- $this->errorMsg = $err;
96
- $trace=debug_backtrace(); $caller=array_shift($trace); wordfence::status(2, 'error', "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
97
- }
98
if(! $res){
99
return false;
100
}
@@ -120,13 +144,9 @@ class wfDB {
120
}
121
$res = mysql_query(call_user_func_array('sprintf', $args), $this->dbh);
122
} else {
123
- wfdie("No arguments passed to query()");
124
- }
125
- $err = mysql_error();
126
- if( (! $isStatusQuery) && $err){ //isStatusQuery prevents loops if status itself is causing error
127
- $this->errorMsg = $err;
128
- $trace=debug_backtrace(); $caller=array_shift($trace); wordfence::status(2, 'error', "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
129
}
130
return $res;
131
}
132
public function queryIgnoreError(){ //sprintfString, arguments
@@ -140,14 +160,15 @@ class wfDB {
140
}
141
$res = mysql_query(call_user_func_array('sprintf', $args), $this->dbh);
142
} else {
143
- wfdie("No arguments passed to query()");
144
}
145
return $res;
146
}
147
148
- private function wfdie($msg){
149
- error_log("Wordfence critical database error: $msg");
150
- exit(1);
151
}
152
public function createKeyIfNotExists($table, $col, $keyName){
153
global $wpdb; $prefix = $wpdb->base_prefix;
16
} else {
17
global $wpdb;
18
if(! $wpdb){
19
+ self::criticalError("The WordPress variable wpdb is not defined. Wordfence can't function without this being defined as it is in all standard WordPress installs.");
20
+ return;
21
+ }
22
+ $sources = array(
23
+ array('dbhost', 'DB_HOST'),
24
+ array('dbuser', 'DB_USER'),
25
+ array('dbpassword', 'DB_PASSWORD'),
26
+ array('dbname', 'DB_NAME')
27
+ );
28
+ foreach($sources as $src){
29
+ $prop = $src[0];
30
+ if(isset($wpdb->$prop)){
31
+ $this->$prop = $wpdb->$prop;
32
+ } else if(defined($src[1])){
33
+ $this->$prop = constant($src[1]);
34
+ } else {
35
+ self::criticalError("Wordfence DB connect error. wpdb.$prop is not set and " . $src[1] . " is not defined.");
36
+ return;
37
+ }
38
}
39
}
40
if($createNewHandle){
41
$dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
42
if($dbh === false){
43
+ self::criticalError("Wordfence could not connect to your database. Error was: " . mysql_error());
44
+ return;
45
}
46
mysql_select_db($this->dbname, $dbh);
47
$this->dbh = $dbh;
56
} else {
57
$dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
58
if($dbh === false){
59
+ self::criticalError("Wordfence could not connect to your database. The error was: " . mysql_error());
60
+ return;
61
}
62
63
mysql_select_db($this->dbname, $dbh);
78
} else if(sizeof($args) > 1){
79
$query = call_user_func_array('sprintf', $args);
80
} else {
81
+ $this->handleError("No arguments passed to querySingle()");
82
}
83
$res = mysql_query($query, $this->dbh);
84
+ $this->handleError();
85
return mysql_fetch_assoc($res); //returns false if no rows found
86
}
87
+ public function handleError($err = false){
88
+ if(! $err){
89
+ $err = mysql_error();
90
+ }
91
+ if($err){
92
+ $trace=debug_backtrace();
93
+ $first=array_shift($trace);
94
+ $caller=array_shift($trace);
95
+ $msg = "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err";
96
+ global $wpdb;
97
+ $statusTable = $wpdb->base_prefix . 'wfStatus';
98
+ mysql_query(sprintf("insert into " . $statusTable . " (ctime, level, type, msg) values (%s, %d, '%s', '%s')",
99
+ mysql_real_escape_string(sprintf('%.6f', microtime(true))),
100
+ mysql_real_escape_string(1),
101
+ mysql_real_escape_string('error'),
102
+ mysql_real_escape_string($msg)), $this->dbh);
103
+ error_log($msg);
104
+ return;
105
+ }
106
+ }
107
public function querySingle(){
108
$this->errorMsg = false;
109
$args = func_get_args();
115
}
116
$query = call_user_func_array('sprintf', $args);
117
} else {
118
+ $this->handleError("No arguments passed to querySingle()");
119
}
120
$res = mysql_query($query, $this->dbh);
121
+ $this->handleError();
122
if(! $res){
123
return false;
124
}
144
}
145
$res = mysql_query(call_user_func_array('sprintf', $args), $this->dbh);
146
} else {
147
+ $this->handleError("No arguments passed to query()");
148
}
149
+ $this->handleError();
150
return $res;
151
}
152
public function queryIgnoreError(){ //sprintfString, arguments
160
}
161
$res = mysql_query(call_user_func_array('sprintf', $args), $this->dbh);
162
} else {
163
+ $this->handleError("No arguments passed to query()");
164
}
165
return $res;
166
}
167
168
+ private static function criticalError($msg){
169
+ $msg = "Wordfence critical database error: $msg";
170
+ error_log($msg);
171
+ return;
172
}
173
public function createKeyIfNotExists($table, $col, $keyName){
174
global $wpdb; $prefix = $wpdb->base_prefix;
lib/wfLog.php CHANGED
@@ -55,7 +55,8 @@ class wfLog {
55
} else if($type == 'hit'){
56
$table = $this->leechTable;
57
} else {
58
- wfUtils::wdie("Invalid type to logLeechAndBlock(): $type");
59
}
60
$IP = wfUtils::getIP();
61
$this->getDB()->query("insert into $table (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfcurrenthits := hits + 1, hits + 1, hits + 1)", wfUtils::inet_aton($IP));
@@ -254,7 +255,8 @@ class wfLog {
254
} else if($type == 'topLeechers'){
255
$table = $this->leechTable;
256
} else {
257
- wfUtils::wdie("Invalid type to getLeechers(): $type");
258
}
259
$res = $this->getDB()->query("select IP, sum(hits) as totalHits from $table where eMin > ((unix_timestamp() - 86400) / 60) group by IP order by totalHits desc limit 20");
260
$results = array();
@@ -313,7 +315,8 @@ class wfLog {
313
} else if($type == 'ruser'){
314
$typeSQL = " and userID > 0 ";
315
} else {
316
- wfUtils::wdie("Invalid log type to wfLog: $type");
317
}
318
319
$r1 = $this->getDB()->query("select * from " . $this->hitsTable . " where ctime > %f $IPSQL $typeSQL order by ctime desc limit %s",
@@ -328,7 +331,8 @@ class wfLog {
328
);
329
330
} else {
331
- wfUtils::wdie("getHits got invalid hitType: $hitType");
332
}
333
$results = array();
334
while($res = mysql_fetch_assoc($r1)){
55
} else if($type == 'hit'){
56
$table = $this->leechTable;
57
} else {
58
+ wordfence::status(1, 'error', "Invalid type to logLeechAndBlock(): $type");
59
+ return;
60
}
61
$IP = wfUtils::getIP();
62
$this->getDB()->query("insert into $table (eMin, IP, hits) values (floor(unix_timestamp() / 60), %s, 1) ON DUPLICATE KEY update hits = IF(@wfcurrenthits := hits + 1, hits + 1, hits + 1)", wfUtils::inet_aton($IP));
255
} else if($type == 'topLeechers'){
256
$table = $this->leechTable;
257
} else {
258
+ wordfence::status(1, 'error', "Invalid type to getLeechers(): $type");
259
+ return false;
260
}
261
$res = $this->getDB()->query("select IP, sum(hits) as totalHits from $table where eMin > ((unix_timestamp() - 86400) / 60) group by IP order by totalHits desc limit 20");
262
$results = array();
315
} else if($type == 'ruser'){
316
$typeSQL = " and userID > 0 ";
317
} else {
318
+ wordfence::status(1, 'error', "Invalid log type to wfLog: $type");
319
+ return false;
320
}
321
322
$r1 = $this->getDB()->query("select * from " . $this->hitsTable . " where ctime > %f $IPSQL $typeSQL order by ctime desc limit %s",
331
);
332
333
} else {
334
+ wordfence::status(1, 'error', "getHits got invalid hitType: $hitType");
335
+ return false;
336
}
337
$results = array();
338
while($res = mysql_fetch_assoc($r1)){
lib/wfScanEngine.php CHANGED
@@ -89,9 +89,10 @@ class wfScanEngine {
89
}
90
}
91
public function fork(){
92
- wfConfig::set_ser('wfsd_engine', $this, true);
93
- wfUtils::clearScanLock();
94
- self::startScan(true);
95
exit(0);
96
}
97
public function emailNewIssues(){
@@ -625,13 +626,15 @@ class wfScanEngine {
625
}
626
private function scan_diskSpace(){
627
$this->statusIDX['diskSpace'] = wordfence::statusStart("Scanning to check available disk space");
628
- $total = disk_total_space('.');
629
- $free = disk_free_space('.');
630
- $this->status(2, 'info', "Total space: $total Free space: $free");
631
if( (! $total) || (! $free )){ //If we get zeros it's probably not reading right. If free is zero then we're out of space and already in trouble.
632
wordfence::statusEnd($this->statusIDX['diskSpace'], false);
633
return;
634
}
635
$level = false;
636
$spaceLeft = sprintf('%.2f', ($free / $total * 100));
637
$this->status(2, 'info', "The disk has $spaceLeft percent space available");
89
}
90
}
91
public function fork(){
92
+ if(wfConfig::set_ser('wfsd_engine', $this, true)){
93
+ wfUtils::clearScanLock();
94
+ self::startScan(true);
95
+ } //Otherwise there was an error so don't start another scan.
96
exit(0);
97
}
98
public function emailNewIssues(){
626
}
627
private function scan_diskSpace(){
628
$this->statusIDX['diskSpace'] = wordfence::statusStart("Scanning to check available disk space");
629
+ wfUtils::errorsOff();
630
+ $total = @disk_total_space('.');
631
+ $free = @disk_free_space('.');
632
+ wfUtils::errorsOn();
633
if( (! $total) || (! $free )){ //If we get zeros it's probably not reading right. If free is zero then we're out of space and already in trouble.
634
wordfence::statusEnd($this->statusIDX['diskSpace'], false);
635
return;
636
}
637
+ $this->status(2, 'info', "Total disk space: " . sprintf('%.4f', ($total / 1024 / 1024 / 1024)) . "GB -- Free disk space: " . sprintf('%.4f', ($free / 1024 / 1024 / 1024)) . "GB");
638
$level = false;
639
$spaceLeft = sprintf('%.2f', ($free / $total * 100));
640
$this->status(2, 'info', "The disk has $spaceLeft percent space available");
lib/wfSchema.php CHANGED
@@ -157,9 +157,9 @@ class wfSchema {
157
$this->prefix = $wpdb->base_prefix;
158
}
159
}
160
- public function dropAll($prefix){
161
foreach($this->tables as $table => $def){
162
- $this->db->query("drop table if exists " . $prefix . $table);
163
}
164
}
165
public function createAll(){
157
$this->prefix = $wpdb->base_prefix;
158
}
159
}
160
+ public function dropAll(){
161
foreach($this->tables as $table => $def){
162
+ $this->db->query("drop table if exists " . $this->prefix . $table);
163
}
164
}
165
public function createAll(){
lib/wfUtils.php CHANGED
@@ -3,6 +3,8 @@ require_once('wfConfig.php');
3
class wfUtils {
4
private static $isWindows = false;
5
public static $scanLockFH = false;
6
public static function makeTimeAgo($secs, $noSeconds = false) {
7
if($secs < 1){
8
return "a moment";
@@ -72,27 +74,41 @@ class wfUtils {
72
//return ABSPATH . 'wp-content/plugins/';
73
}
74
public static function getIP(){
75
- $ip = 0;
76
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
77
- $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
78
}
79
- if((! $ip) && isset($_SERVER['REMOTE_ADDR'])){
80
- $ip = $_SERVER['REMOTE_ADDR'];
81
}
82
- if(preg_match('/,/', $ip)){
83
- $parts = explode(',', $ip);
84
- $ip = trim($parts[0]);
85
}
86
- if(preg_match('/:\d+#x2F;', $ip)){
87
- $ip = preg_replace('/:\d+#x2F;', '', $ip);
88
}
89
- if(self::isValidIP($ip)){
90
- return $ip;
91
} else {
92
- $msg = "Wordfence is not able to determine the IP addresses of visitors to your site and can't operate. We received IP: $ip from header1: " . $_SERVER['HTTP_X_FORWARDED_FOR'] . " and header2: " . $_SERVER['REMOTE_ADDR'];
93
wordfence::status(1, 'error', $msg);
94
error_log($msg);
95
- exit(0);
96
}
97
}
98
public static function isValidIP($IP){
@@ -115,11 +131,6 @@ class wfUtils {
115
public static function editUserLink($userID){
116
return get_admin_url() . 'user-edit.php?user_id=' . $userID;
117
}
118
- public static function wdie($err){
119
- $trace=debug_backtrace(); $caller=array_shift($trace);
120
- error_log("Wordfence error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
121
- exit();
122
- }
123
public static function tmpl($file, $data){
124
extract($data);
125
ob_start();
@@ -132,8 +143,8 @@ class wfUtils {
132
public static function encrypt($str){
133
$key = wfConfig::get('encKey');
134
if(! $key){
135
- error_log("Wordfence error: No encryption key found!");
136
- exit();
137
}
138
$db = new wfDB();
139
return $db->querySingle("select HEX(AES_ENCRYPT('%s', '%s')) as val", $str, $key);
@@ -141,8 +152,8 @@ class wfUtils {
141
public static function decrypt($str){
142
$key = wfConfig::get('encKey');
143
if(! $key){
144
- error_log("Wordfence error: No encryption key found!");
145
- exit();
146
}
147
$db = new wfDB();
148
return $db->querySingle("select AES_DECRYPT(UNHEX('%s'), '%s') as val", $str, $key);
@@ -362,6 +373,18 @@ class wfUtils {
362
return $host;
363
}
364
}
365
}
366
367
3
class wfUtils {
4
private static $isWindows = false;
5
public static $scanLockFH = false;
6
+ private static $lastErrorReporting = false;
7
+ private static $lastDisplayErrors = false;
8
public static function makeTimeAgo($secs, $noSeconds = false) {
9
if($secs < 1){
10
return "a moment";
74
//return ABSPATH . 'wp-content/plugins/';
75
}
76
public static function getIP(){
77
+ $IP = 0;
78
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
79
+ $IP = $_SERVER['HTTP_X_FORWARDED_FOR'];
80
}
81
+ if((! preg_match('/(\d+)\.(\d+)\.(\d+)\.(\d+)/', $IP)) && isset($_SERVER['REMOTE_ADDR'])){
82
+ $IP = $_SERVER['REMOTE_ADDR'];
83
}
84
+ if(preg_match('/,/', $IP)){
85
+ $parts = explode(',', $IP);
86
+ $IP = trim($parts[0]);
87
}
88
+ if(preg_match('/:\d+#x2F;', $IP)){
89
+ $IP = preg_replace('/:\d+#x2F;', '', $IP);
90
}
91
+ if(self::isValidIP($IP)){
92
+ return $IP;
93
} else {
94
+ $msg = "Wordfence can't get the IP of clients and therefore can't operate. We received IP: $IP. X-Forwarded-For was: " . $_SERVER['HTTP_X_FORWARDED_FOR'] . " REMOTE_ADDR was: " . $_SERVER['REMOTE_ADDR'];
95
+ $possible = array();
96
+ foreach($_SERVER as $key => $val){
97
+ if(preg_match('/^\d+\.\d+\.\d+\.\d+/', $val) && strlen($val) < 255){
98
+ if($val != '127.0.0.1'){
99
+ $possible[$key] = $val;
100
+ }
101
+ }
102
+ }
103
+ if(sizeof($possible) > 0){
104
+ $msg .= " Report the following on the Wordfence forums and they may be able to help. Headers that may contain the client IP: ";
105
+ foreach($possible as $key => $val){
106
+ $msg .= "$key => $val ";
107
+ }
108
+ }
109
wordfence::status(1, 'error', $msg);
110
error_log($msg);
111
+ return false;
112
}
113
}
114
public static function isValidIP($IP){
131
public static function editUserLink($userID){
132
return get_admin_url() . 'user-edit.php?user_id=' . $userID;
133
}
134
public static function tmpl($file, $data){
135
extract($data);
136
ob_start();
143
public static function encrypt($str){
144
$key = wfConfig::get('encKey');
145
if(! $key){
146
+ wordfence::status(1, 'error', "Wordfence error: No encryption key found!");
147
+ return false;
148
}
149
$db = new wfDB();
150
return $db->querySingle("select HEX(AES_ENCRYPT('%s', '%s')) as val", $str, $key);
152
public static function decrypt($str){
153
$key = wfConfig::get('encKey');
154
if(! $key){
155
+ wordfence::status(1, 'error', "Wordfence error: No encryption key found!");
156
+ return false;
157
}
158
$db = new wfDB();
159
return $db->querySingle("select AES_DECRYPT(UNHEX('%s'), '%s') as val", $str, $key);
373
return $host;
374
}
375
}
376
+ public static function errorsOff(){
377
+ self::$lastErrorReporting = @ini_get('error_reporting');
378
+ @error_reporting(0);
379
+ self::$lastDisplayErrors = @ini_get('display_errors');
380
+ @ini_set('display_errors', 0);
381
+ if(class_exists('wfScan')){ wfScan::$errorHandlingOn = false; }
382
+ }
383
+ public static function errorsOn(){
384
+ @error_reporting(self::$lastErrorReporting);
385
+ @ini_set('display_errors', self::$lastDisplayErrors);
386
+ if(class_exists('wfScan')){ wfScan::$errorHandlingOn = true; }
387
+ }
388
}
389
390
lib/wordfenceClass.php CHANGED
@@ -34,6 +34,11 @@ class wordfence {
34
wp_clear_scheduled_hook('wordfence_daily_cron');
35
wp_clear_scheduled_hook('wordfence_hourly_cron');
36
wp_clear_scheduled_hook('wordfence_scheduled_scan');
37
}
38
public static function hourlyCron(){
39
global $wpdb; $p = $wpdb->base_prefix;
@@ -187,7 +192,7 @@ class wordfence {
187
$db->queryIgnoreError("alter table $prefix"."wfConfig modify column val longblob");
188
$db->queryIgnoreError("alter table $prefix"."wfBlocks add column permanent tinyint UNSIGNED default 0");
189
$db->queryIgnoreError("alter table $prefix"."wfStatus modify column msg varchar(1000) NOT NULL");
190
-
191
//Must be the final line
192
update_option('wordfence_version', WORDFENCE_VERSION);
193
}
@@ -202,12 +207,6 @@ class wordfence {
202
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?!)
203
}
204
205
- //Upgrading from 2.0.3 we changed isPaid from 'free' or 'paid' to true and false
206
- if(wfConfig::get('isPaid') == 'free'){
207
- wfConfig::set('isPaid', '');
208
- }
209
- //end
210
-
211
add_action('wordfence_daily_cron', 'wordfence::dailyCron');
212
add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
213
add_action('plugins_loaded', 'wordfence::veryFirstAction');
34
wp_clear_scheduled_hook('wordfence_daily_cron');
35
wp_clear_scheduled_hook('wordfence_hourly_cron');
36
wp_clear_scheduled_hook('wordfence_scheduled_scan');
37
+ $schema = new wfSchema();
38
+ $schema->dropAll();
39
+ foreach(array('wordfence_version', 'wordfenceActivated') as $opt){
40
+ delete_option($opt);
41
+ }
42
}
43
public static function hourlyCron(){
44
global $wpdb; $p = $wpdb->base_prefix;
192
$db->queryIgnoreError("alter table $prefix"."wfConfig modify column val longblob");
193
$db->queryIgnoreError("alter table $prefix"."wfBlocks add column permanent tinyint UNSIGNED default 0");
194
$db->queryIgnoreError("alter table $prefix"."wfStatus modify column msg varchar(1000) NOT NULL");
195
+
196
//Must be the final line
197
update_option('wordfence_version', WORDFENCE_VERSION);
198
}
207
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?!)
208
}
209
210
add_action('wordfence_daily_cron', 'wordfence::dailyCron');
211
add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
212
add_action('plugins_loaded', 'wordfence::veryFirstAction');
lib/wordfenceHash.php CHANGED
@@ -26,7 +26,7 @@ class wordfenceHash {
26
$path .= '/';
27
}
28
if(! is_readable($path)){
29
- wordfence::status(1, 'error', "Could not read directory $path to do sacn.");
30
exit();
31
}
32
$files = scandir($path);
26
$path .= '/';
27
}
28
if(! is_readable($path)){
29
+ wordfence::status(1, 'error', "Could not read directory $path to do scan.");
30
exit();
31
}
32
$files = scandir($path);
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
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.3.2
6
- Stable tag: 2.1.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
@@ -152,6 +152,25 @@ or a theme, because often these have been updated to fix a security hole.
152
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
153
154
== Changelog ==
155
= 2.1.4 =
156
* Fixed registered users not appearing in live traffic.
157
* Fixed temp file deletion bug that caused warnings and loops.
2
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
6
+ Stable tag: 2.1.5
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
152
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
153
154
== Changelog ==
155
+ = 2.1.5 =
156
+ * Fixed bug that caused WF to not work when certain DB caching plugins are used and override wpdb object.
157
+ * Fixed Wordfence so activity log only shows our own errors unless in debug mode.
158
+ * Wordfence now deletes all it's tables and deletes all saved options when you deactivate the plugin.
159
+ * Removed all exit() on error statements. Critical errors are handled more gracefully by writing to the log instead.
160
+ * Fixed a bug that would cause a database loop until running out of memory under certain error conditions.
161
+ * Suppressed useless warnings that occur in environments with basedir set or where functions are disabled for security reasons.
162
+ * Removed redundant check that executed on every request and put it in activation instead.
163
+ * If serialization during scan breaks, exit gracefully instead of looping.
164
+ * Disk space in log is now shown as Gigabytes and formatted nicely.
165
+ * Removed wdie() function which is a little obnoxious. Writing to WF error log instead.
166
+ * Fixed bug where a non-empty but useless HTTP header can break getIP() function.
167
+ * Added useful data to error output if getIP() tells you it can't work on your system.
168
+ * Removed option to start scan in debug because it's no longer possible with a forked scan.
169
+ * Removed option to test process running time on a system because it breaks on most systems and confuses customers.
170
+ * Database connection errors no longer call die() but log an error instead in a way that removes the risk of a logging loop.
171
+ * Removed dropAll.php script because we now clean up tables on deactivate and it's not needed.
172
+ * Updated readme to show that we support 3.4.
173
+
174
= 2.1.4 =
175
* Fixed registered users not appearing in live traffic.
176
* Fixed temp file deletion bug that caused warnings and loops.
wfscan.php CHANGED
@@ -23,6 +23,7 @@ require_once('lib/wfScanEngine.php');
23
24
class wfScan {
25
public static $debugMode = false;
26
public static function wfScanMain(){
27
$db = new wfDB();
28
if($db->errorMsg){
@@ -112,7 +113,14 @@ class wfScan {
112
}
113
}
114
public static function error_handler($errno, $errstr, $errfile, $errline){
115
- wordfence::status(1, 'error', "$errstr ($errno) File: $errfile Line: $errline");
116
}
117
public static function shutdown(){
118
wfUtils::clearScanLock();
23
24
class wfScan {
25
public static $debugMode = false;
26
+ public static $errorHandlingOn = true;
27
public static function wfScanMain(){
28
$db = new wfDB();
29
if($db->errorMsg){
113
}
114
}
115
public static function error_handler($errno, $errstr, $errfile, $errline){
116
+ if(self::$errorHandlingOn){
117
+ if(preg_match('/wordfence\//', $errfile)){
118
+ $level = 1; //It's one of our files, so level 1
119
+ } else {
120
+ $level = 4; //It's someone elses plugin so only show if debug is enabled
121
+ }
122
+ wordfence::status($level, 'error', "$errstr ($errno) File: $errfile Line: $errline");
123
+ }
124
}
125
public static function shutdown(){
126
wfUtils::clearScanLock();
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: 2.1.4
8
Author URI: http://wordfence.com/
9
*/
10
- define('WORDFENCE_VERSION', '2.1.4');
11
if(! defined('WORDFENCE_VERSIONONLY_MODE')){
12
require_once('lib/wordfenceConstants.php');
13
require_once('lib/wordfenceClass.php');
4
Plugin URI: http://wordfence.com/
5
Description: Wordfence Security - Anti-virus and Firewall security plugin for WordPress
6
Author: Mark Maunder
7
+ Version: 2.1.5
8
Author URI: http://wordfence.com/
9
*/
10
+ define('WORDFENCE_VERSION', '2.1.5');
11
if(! defined('WORDFENCE_VERSIONONLY_MODE')){
12
require_once('lib/wordfenceConstants.php');
13
require_once('lib/wordfenceClass.php');