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');