Wordfence Security – Firewall & Malware Scan - Version 2.1.3

Version Description

Download this release

Release Info

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

Code changes from version 2.1.2 to 2.1.3

lib/wfConfig.php CHANGED
@@ -3,6 +3,7 @@ class wfConfig {
3
private static $table = false;
4
private static $cache = array();
5
private static $DB = false;
6
public static $securityLevels = array(
7
array( //level 0
8
"checkboxes" => array(
@@ -366,24 +367,8 @@ class wfConfig {
366
public static function clearCache(){
367
self::$cache = array();
368
}
369
- public static function set_ser($key, $val){
370
- //We serialize some very big values so this is ultra-memory efficient. We don't make any copies of $val and don't use ON DUPLICATE KEY UPDATE
371
- // because we would have to concatenate $val twice into the query which could also exceed max packet for the mysql server
372
- $dbh = self::getDB()->getDBH();
373
- $exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key);
374
- if($exists){
375
- $res = mysql_query("update " . self::table() . " set val='" . mysql_real_escape_string(serialize($val)) . "' where name='" . mysql_real_escape_string($key) . "'", $dbh);
376
- } else {
377
- $res = mysql_query("insert IGNORE into " . self::table() . " (name, val) values ('" . mysql_real_escape_string($key) . "', '" . mysql_real_escape_string(serialize($val)) . "')", $dbh);
378
- }
379
- $err = mysql_error();
380
- if($err){
381
- $trace=debug_backtrace();
382
- $caller=array_shift($trace);
383
- wordfence::status(2, 'error', "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
384
- return false;
385
- }
386
- return true;
387
}
388
public static function set($key, $val){
389
if(is_array($val)){
@@ -393,9 +378,6 @@ class wfConfig {
393
self::getDB()->query("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
394
self::$cache[$key] = $val;
395
}
396
- public static function getHTML($key){
397
- return htmlspecialchars(self::get($key));
398
- }
399
public static function get($key, $default = false){
400
if(! isset(self::$cache[$key])){
401
$val = self::getDB()->querySingle("select val from " . self::table() . " where name='%s'", $key);
@@ -407,7 +389,35 @@ class wfConfig {
407
}
408
return self::$cache[$key];
409
}
410
- public static function get_ser($key, $default){
411
$dbh = self::getDB()->getDBH();
412
$res = mysql_query("select val from " . self::table() . " where name='" . mysql_real_escape_string($key) . "'", $dbh);
413
$err = mysql_error();
@@ -424,6 +434,70 @@ class wfConfig {
424
}
425
return $default;
426
}
427
public static function f($key){
428
echo esc_attr(self::get($key));
429
}
3
private static $table = false;
4
private static $cache = array();
5
private static $DB = false;
6
+ private static $tmpFileHeader = "<?php\n/* Wordfence temporary file security header */\necho \"Nothing to see here!\\n\"; exit(0);\n?>";
7
public static $securityLevels = array(
8
array( //level 0
9
"checkboxes" => array(
367
public static function clearCache(){
368
self::$cache = array();
369
}
370
+ public static function getHTML($key){
371
+ return htmlspecialchars(self::get($key));
372
}
373
public static function set($key, $val){
374
if(is_array($val)){
378
self::getDB()->query("insert into " . self::table() . " (name, val) values ('%s', '%s') ON DUPLICATE KEY UPDATE val='%s'", $key, $val, $val);
379
self::$cache[$key] = $val;
380
}
381
public static function get($key, $default = false){
382
if(! isset(self::$cache[$key])){
383
$val = self::getDB()->querySingle("select val from " . self::table() . " where name='%s'", $key);
389
}
390
return self::$cache[$key];
391
}
392
+ public static function get_ser($key, $default, $canUseDisk = false){ //When using disk, reading a value deletes it.
393
+ //If we can use disk, check if there are any values stored on disk first and read them instead of the DB if there are values
394
+ if($canUseDisk){
395
+ $filename = 'wordfence_tmpfile_' . $key . '.php';
396
+ $dirs = self::getTempDirs();
397
+ $obj = false;
398
+ $foundFiles = false;
399
+ foreach($dirs as $dir){
400
+ $dir = rtrim($dir, '/') . '/';
401
+ $fullFile = $dir . $filename;
402
+ if(file_exists($fullFile)){
403
+ $foundFiles = true;
404
+ wordfence::status(4, 'info', "Loading serialized data from file $fullFile");
405
+ $obj = unserialize(substr(file_get_contents($fullFile), strlen(self::$tmpFileHeader))); //Strip off security header and unserialize
406
+ if($obj){
407
+ break;
408
+ } else {
409
+ wordfence::status(2, 'error', "Could not unserialize file $fullFile");
410
+ }
411
+ }
412
+ }
413
+ if($foundFiles){
414
+ self::deleteOldTempFiles($filename);
415
+ }
416
+ if($obj){ //If we managed to deserialize something, clean ALL tmp dirs of this file and return obj
417
+ return $obj;
418
+ }
419
+ }
420
+
421
$dbh = self::getDB()->getDBH();
422
$res = mysql_query("select val from " . self::table() . " where name='" . mysql_real_escape_string($key) . "'", $dbh);
423
$err = mysql_error();
434
}
435
return $default;
436
}
437
+ public static function set_ser($key, $val, $canUseDisk = false){
438
+ //We serialize some very big values so this is ultra-memory efficient. We don't make any copies of $val and don't use ON DUPLICATE KEY UPDATE
439
+ // because we would have to concatenate $val twice into the query which could also exceed max packet for the mysql server
440
+ $dbh = self::getDB()->getDBH();
441
+ $serialized = serialize($val);
442
+ $tempFilename = 'wordfence_tmpfile_' . $key . '.php';
443
+ if((strlen($serialized) * 1.1) > self::getDB()->getMaxAllowedPacketBytes()){ //If it's greater than max_allowed_packet + 10% for escaping and SQL
444
+ if($canUseDisk){
445
+ self::deleteOldTempFiles($tempFilename);
446
+ $dirs = self::getTempDirs();
447
+ $fh = false;
448
+ foreach($dirs as $dir){
449
+ $dir = rtrim($dir, '/') . '/';
450
+ $fullFile = $dir . $tempFilename;
451
+ $fh = fopen($fullFile, 'w');
452
+ if($fh){
453
+ 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);
454
+ break;
455
+ }
456
+ }
457
+ if(! $fh){
458
+ 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));
459
+ exit();
460
+ }
461
+ fwrite($fh, self::$tmpFileHeader);
462
+ fwrite($fh, $serialized);
463
+ fclose($fh);
464
+ return true;
465
+ } else {
466
+ 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.");
467
+ exit(0);
468
+ }
469
+ } else {
470
+ //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
471
+ self::deleteOldTempFiles($tempFilename);
472
+ $exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key);
473
+ if($exists){
474
+ $res = mysql_query("update " . self::table() . " set val='" . mysql_real_escape_string($serialized) . "' where name='" . mysql_real_escape_string($key) . "'", $dbh);
475
+ } else {
476
+ $res = mysql_query("insert IGNORE into " . self::table() . " (name, val) values ('" . mysql_real_escape_string($key) . "', '" . mysql_real_escape_string($serialized) . "')", $dbh);
477
+ }
478
+ $err = mysql_error();
479
+ if($err){
480
+ $trace=debug_backtrace();
481
+ $caller=array_shift($trace);
482
+ wordfence::status(2, 'error', "Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
483
+ return false;
484
+ }
485
+ }
486
+ return true;
487
+ }
488
+ private static function deleteOldTempFiles($filename){
489
+ $dirs = self::getTempDirs();
490
+ foreach($dirs as &$dir){ //clean out old files in all dirs
491
+ $dir = rtrim($dir, '/') . '/';
492
+ $fullFile = $dir . $filename;
493
+ if(file_exists($fullFile)){
494
+ unlink($fullFile);
495
+ }
496
+ }
497
+ }
498
+ private static function getTempDirs(){
499
+ return array(sys_get_temp_dir(), wfUtils::getPluginBaseDir() . 'wordfence/tmp/');
500
+ }
501
public static function f($key){
502
echo esc_attr(self::get($key));
503
}
lib/wfDB.php CHANGED
@@ -36,7 +36,7 @@ class wfDB {
36
$this->dbh = $dbh;
37
$this->query("SET NAMES 'utf8'");
38
//Set big packets for set_ser when it serializes a scan in between forks
39
- $this->queryIgnoreError("SET GLOBAL max_allowed_packet=256*1024*1024");
40
} else {
41
$handleKey = md5($dbhost . $dbuser . $dbpassword . $dbname);
42
if(isset(self::$dbhCache[$handleKey])){
@@ -52,7 +52,7 @@ class wfDB {
52
$this->dbh = self::$dbhCache[$handleKey];
53
$this->query("SET NAMES 'utf8'");
54
//Set big packets for set_ser when it serializes a scan in between forks
55
- $this->queryIgnoreError("SET GLOBAL max_allowed_packet=256*1024*1024");
56
}
57
}
58
}
@@ -167,6 +167,10 @@ class wfDB {
167
}
168
}
169
public function getDBH(){ return $this->dbh; }
170
}
171
172
?>
36
$this->dbh = $dbh;
37
$this->query("SET NAMES 'utf8'");
38
//Set big packets for set_ser when it serializes a scan in between forks
39
+ //$this->queryIgnoreError("SET GLOBAL max_allowed_packet=256*1024*1024");
40
} else {
41
$handleKey = md5($dbhost . $dbuser . $dbpassword . $dbname);
42
if(isset(self::$dbhCache[$handleKey])){
52
$this->dbh = self::$dbhCache[$handleKey];
53
$this->query("SET NAMES 'utf8'");
54
//Set big packets for set_ser when it serializes a scan in between forks
55
+ //$this->queryIgnoreError("SET GLOBAL max_allowed_packet=256*1024*1024");
56
}
57
}
58
}
167
}
168
}
169
public function getDBH(){ return $this->dbh; }
170
+ public function getMaxAllowedPacketBytes(){
171
+ $rec = $this->querySingleRec("show variables like 'max_allowed_packet'");
172
+ return $rec['Value'];
173
+ }
174
}
175
176
?>
lib/wfScanEngine.php CHANGED
@@ -89,7 +89,7 @@ class wfScanEngine {
89
}
90
}
91
public function fork(){
92
- wfConfig::set_ser('wfsd_engine', $this);
93
wfUtils::clearScanLock();
94
self::startScan(true);
95
exit(0);
89
}
90
}
91
public function fork(){
92
+ wfConfig::set_ser('wfsd_engine', $this, true);
93
wfUtils::clearScanLock();
94
self::startScan(true);
95
exit(0);
lib/wfSchema.php CHANGED
@@ -126,7 +126,7 @@ class wfSchema {
126
ctime DOUBLE(17,6) UNSIGNED NOT NULL,
127
level tinyint UNSIGNED NOT NULL,
128
type char(5) NOT NULL,
129
- msg varchar(255) NOT NULL,
130
KEY k1(ctime),
131
KEY k2(type)
132
) default charset=utf8",
126
ctime DOUBLE(17,6) UNSIGNED NOT NULL,
127
level tinyint UNSIGNED NOT NULL,
128
type char(5) NOT NULL,
129
+ msg varchar(1000) NOT NULL,
130
KEY k1(ctime),
131
KEY k2(type)
132
) default charset=utf8",
lib/wordfenceClass.php CHANGED
@@ -186,6 +186,7 @@ class wordfence {
186
$prefix = $wpdb->base_prefix;
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
190
//Must be the final line
191
update_option('wordfence_version', WORDFENCE_VERSION);
186
$prefix = $wpdb->base_prefix;
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);
readme.txt CHANGED
@@ -152,6 +152,10 @@ 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.2 =
156
* Fixed issue with scan scheduling that caused a loop
157
* Fixed issue that caused version constant to not be included in scans
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.3: June 5, 2012 dev release =
156
+ * Added fix for hosts that have max_allowed_packet set too small. We will write a temp file to disk instead if possible.
157
+ * Increased size of status column to 1000 chars
158
+
159
= 2.1.2 =
160
* Fixed issue with scan scheduling that caused a loop
161
* Fixed issue that caused version constant to not be included in scans
tmp/.htaccess ADDED
@@ -0,0 +1,2 @@
1
+ deny from all
2
+
wfscan.php CHANGED
@@ -85,15 +85,15 @@ class wfScan {
85
@ini_set('display_errors','On');
86
wordfence::status(4, 'info', "Setting up scanRunning and starting scan");
87
$isFork = ($_GET['isFork'] == '1' ? true : false);
88
- $scan = wfConfig::get_ser('wfsd_engine', false);
89
if($scan){
90
//Set false so that we don't get stuck in a loop where we're repeating scan stages.
91
wordfence::status(4, 'info', "Got a true deserialized value back from 'wfsd_engine' with type: " . gettype($scan));
92
- wfConfig::set('wfsd_engine', '');
93
} else {
94
if($isFork){ //We encountered an error so blank scan and exit
95
wordfence::status(2, 'error', "Scan can't continue - stored data not found after a fork. Got type: " . gettype($scan));
96
- wfConfig::set('wfsd_engine', '');
97
exit();
98
} else {
99
wordfence::statusPrep(); //Re-initializes all status counters
85
@ini_set('display_errors','On');
86
wordfence::status(4, 'info', "Setting up scanRunning and starting scan");
87
$isFork = ($_GET['isFork'] == '1' ? true : false);
88
+ $scan = wfConfig::get_ser('wfsd_engine', false, true);
89
if($scan){
90
//Set false so that we don't get stuck in a loop where we're repeating scan stages.
91
wordfence::status(4, 'info', "Got a true deserialized value back from 'wfsd_engine' with type: " . gettype($scan));
92
+ wfConfig::set('wfsd_engine', '', true);
93
} else {
94
if($isFork){ //We encountered an error so blank scan and exit
95
wordfence::status(2, 'error', "Scan can't continue - stored data not found after a fork. Got type: " . gettype($scan));
96
+ wfConfig::set('wfsd_engine', '', true);
97
exit();
98
} else {
99
wordfence::statusPrep(); //Re-initializes all status counters
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.2
8
Author URI: http://wordfence.com/
9
*/
10
- define('WORDFENCE_VERSION', '2.1.2');
11
12
if(! defined('WORDFENCE_SCAN_MODE')){
13
require_once('lib/wordfenceConstants.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.3
8
Author URI: http://wordfence.com/
9
*/
10
+ define('WORDFENCE_VERSION', '2.1.3');
11
12
if(! defined('WORDFENCE_SCAN_MODE')){
13
require_once('lib/wordfenceConstants.php');