Wordfence Security – Firewall & Malware Scan - Version 1.3.2

Version Description

  • Reduced the number of database connections that Wordfence makes to one.
  • Modified the memory efficient unbuffered queries we use to only use a single DB connection.
  • Removed status updates during post and comment scans which prevents interference with unbuffered queries and makes the scans even faster.
Download this release

Release Info

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

Code changes from version 1.3.1 to 1.3.2

Files changed (5) hide show
  1. lib/wfDB.php +21 -16
  2. lib/wfScanEngine.php +2 -3
  3. lib/wfSchema.php +1 -1
  4. readme.txt +6 -1
  5. wordfence.php +1 -1
lib/wfDB.php CHANGED
@@ -1,11 +1,12 @@
1
  <?php
2
  class wfDB {
3
  private $dbh = false;
 
4
  private $dbhost = false;
5
  private $dbpassword = false;
6
  private $dbname = false;
7
  private $dbuser = false;
8
- public function __construct($dbhost = false, $dbuser = false, $dbpassword = false, $dbname = false){
9
  if($dbhost && $dbuser && $dbpassword && $dbname){
10
  $this->dbhost = $dbhost;
11
  $this->dbuser = $dbuser;
@@ -19,7 +20,20 @@ class wfDB {
19
  $this->dbpassword = $wpdb->dbpassword;
20
  $this->dbname = $wpdb->dbname;
21
  }
22
- $this->getDBH(); //or mysql_real_escape_string won't work
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
  public function querySingleRec(){
25
  $args = func_get_args();
@@ -30,7 +44,7 @@ class wfDB {
30
  } else {
31
  wfdie("No arguments passed to querySingle()");
32
  }
33
- $res = mysql_query($query, $this->getDBH());
34
  $err = mysql_error();
35
  if($err){
36
  $trace=debug_backtrace(); $caller=array_shift($trace); error_log("Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
@@ -49,7 +63,7 @@ class wfDB {
49
  } else {
50
  wfdie("No arguments passed to querySingle()");
51
  }
52
- $res = mysql_query($query, $this->getDBH());
53
  $err = mysql_error();
54
  if($err){
55
  $trace=debug_backtrace(); $caller=array_shift($trace); error_log("Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
@@ -73,14 +87,14 @@ class wfDB {
73
  } else {
74
  wfdie("No arguments passed to query()");
75
  }
76
- $res = mysql_query($query, $this->getDBH());
77
  $err = mysql_error();
78
  if($err){
79
  $trace=debug_backtrace(); $caller=array_shift($trace); error_log("Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
80
  }
81
  return $res;
82
  }
83
- public function uQuery(){ //sprintfString, arguments
84
  $args = func_get_args();
85
  if(sizeof($args) == 1){
86
  $query = $args[0];
@@ -92,22 +106,13 @@ class wfDB {
92
  } else {
93
  wfdie("No arguments passed to query()");
94
  }
95
- $res = mysql_unbuffered_query($query, $this->getDBH());
96
  $err = mysql_error();
97
  if($err){
98
  $trace=debug_backtrace(); $caller=array_shift($trace); error_log("Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
99
  }
100
  return $res;
101
  }
102
- private function getDBH(){
103
- if($this->dbh){
104
- return $this->dbh;
105
- }
106
- $dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
107
- mysql_select_db($this->dbname, $dbh);
108
- $this->dbh = $dbh;
109
- return $dbh;
110
- }
111
  private function wfdie($msg){
112
  error_log($msg);
113
  exit(1);
1
  <?php
2
  class wfDB {
3
  private $dbh = false;
4
+ private static $dbhCache = false;
5
  private $dbhost = false;
6
  private $dbpassword = false;
7
  private $dbname = false;
8
  private $dbuser = false;
9
+ public function __construct($createNewHandle = false, $dbhost = false, $dbuser = false, $dbpassword = false, $dbname = false){
10
  if($dbhost && $dbuser && $dbpassword && $dbname){
11
  $this->dbhost = $dbhost;
12
  $this->dbuser = $dbuser;
20
  $this->dbpassword = $wpdb->dbpassword;
21
  $this->dbname = $wpdb->dbname;
22
  }
23
+ if($createNewHandle){
24
+ $dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
25
+ mysql_select_db($this->dbname, $dbh);
26
+ $this->dbh = $dbh;
27
+ } else {
28
+ if(self::$dbhCache){
29
+ $this->dbh = self::$dbhCache;
30
+ } else {
31
+ $dbh = mysql_connect( $this->dbhost, $this->dbuser, $this->dbpassword, true );
32
+ mysql_select_db($this->dbname, $dbh);
33
+ self::$dbhCache = $dbh;
34
+ $this->dbh = self::$dbhCache;
35
+ }
36
+ }
37
  }
38
  public function querySingleRec(){
39
  $args = func_get_args();
44
  } else {
45
  wfdie("No arguments passed to querySingle()");
46
  }
47
+ $res = mysql_query($query, $this->dbh);
48
  $err = mysql_error();
49
  if($err){
50
  $trace=debug_backtrace(); $caller=array_shift($trace); error_log("Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
63
  } else {
64
  wfdie("No arguments passed to querySingle()");
65
  }
66
+ $res = mysql_query($query, $this->dbh);
67
  $err = mysql_error();
68
  if($err){
69
  $trace=debug_backtrace(); $caller=array_shift($trace); error_log("Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
87
  } else {
88
  wfdie("No arguments passed to query()");
89
  }
90
+ $res = mysql_query($query, $this->dbh);
91
  $err = mysql_error();
92
  if($err){
93
  $trace=debug_backtrace(); $caller=array_shift($trace); error_log("Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
94
  }
95
  return $res;
96
  }
97
+ public function uQuery(){ //sprintfString, arguments NOTE: Very important that there is no other DB activity between uQuery and when you call mysql_free_result on the return value of uQuery.
98
  $args = func_get_args();
99
  if(sizeof($args) == 1){
100
  $query = $args[0];
106
  } else {
107
  wfdie("No arguments passed to query()");
108
  }
109
+ $res = mysql_unbuffered_query($query, $this->dbh);
110
  $err = mysql_error();
111
  if($err){
112
  $trace=debug_backtrace(); $caller=array_shift($trace); error_log("Wordfence DB error in " . $caller['file'] . " line " . $caller['line'] . ": $err");
113
  }
114
  return $res;
115
  }
 
 
 
 
 
 
 
 
 
116
  private function wfdie($msg){
117
  error_log($msg);
118
  exit(1);
lib/wfScanEngine.php CHANGED
@@ -282,13 +282,12 @@ class wfScanEngine {
282
  $this->status(1, 'info', "Starting posts scan");
283
  global $wpdb;
284
  $wfdb = new wfDB();
 
285
  $q1 = $wfdb->uQuery("select ID, post_title, post_type, post_date, post_content from $wpdb->posts where post_type IN ('page', 'post') and post_status = 'publish'");
286
  $h = new wordfenceURLHoover($this->apiKey, $this->wp_version);
287
  $postDat = array();
288
  while($row = mysql_fetch_assoc($q1)){
289
- $this->status(2, 'info', "Scanning " . $row['post_type'] . ": \"" . $row['post_title'] . "\"");
290
  $h->hoover($row['ID'], $row['post_title'] . ' ' . $row['post_content']);
291
-
292
  $postDat[$row['ID']] = array(
293
  'contentMD5' => md5($row['post_content']),
294
  'title' => $row['post_title'],
@@ -372,6 +371,7 @@ class wfScanEngine {
372
  private function scanComments(){
373
  global $wpdb;
374
  $wfdb = new wfDB();
 
375
  $q1 = $wfdb->uQuery("select comment_ID, comment_date, comment_type, comment_author, comment_author_url, comment_content from $wpdb->comments where comment_approved=1");
376
  if( ! $q1){
377
  return;
@@ -381,7 +381,6 @@ class wfScanEngine {
381
  $gotRow = false;
382
  while($row = mysql_fetch_assoc($q1)){
383
  $gotRow = true; //because we can't use mysql_num_rows on unbuffered queries
384
- $this->status(2, 'info', "Scanning comment ID " . $row['comment_ID'] . " with author " . $row['comment_author']);
385
  $h->hoover($row['comment_ID'], $row['comment_author_url'] . ' ' . $row['comment_author'] . ' ' . $row['comment_content']);
386
  $commentDat[$row['comment_ID']] = array(
387
  'contentMD5' => md5($row['comment_content'] . $row['comment_author'] . $row['comment_author_url']),
282
  $this->status(1, 'info', "Starting posts scan");
283
  global $wpdb;
284
  $wfdb = new wfDB();
285
+ //NOTE: There must be no other DB activity by wfDB between here and free_result below because we're doing an unbuffered query. THAT INCLUDES calls to status() which updates the DB
286
  $q1 = $wfdb->uQuery("select ID, post_title, post_type, post_date, post_content from $wpdb->posts where post_type IN ('page', 'post') and post_status = 'publish'");
287
  $h = new wordfenceURLHoover($this->apiKey, $this->wp_version);
288
  $postDat = array();
289
  while($row = mysql_fetch_assoc($q1)){
 
290
  $h->hoover($row['ID'], $row['post_title'] . ' ' . $row['post_content']);
 
291
  $postDat[$row['ID']] = array(
292
  'contentMD5' => md5($row['post_content']),
293
  'title' => $row['post_title'],
371
  private function scanComments(){
372
  global $wpdb;
373
  $wfdb = new wfDB();
374
+ //NOTE: There must be no other DB activity by wfDB between here and free_result below because we're doing an unbuffered query. THAT INCLUDES calls to status() which updates the DB
375
  $q1 = $wfdb->uQuery("select comment_ID, comment_date, comment_type, comment_author, comment_author_url, comment_content from $wpdb->comments where comment_approved=1");
376
  if( ! $q1){
377
  return;
381
  $gotRow = false;
382
  while($row = mysql_fetch_assoc($q1)){
383
  $gotRow = true; //because we can't use mysql_num_rows on unbuffered queries
 
384
  $h->hoover($row['comment_ID'], $row['comment_author_url'] . ' ' . $row['comment_author'] . ' ' . $row['comment_content']);
385
  $commentDat[$row['comment_ID']] = array(
386
  'contentMD5' => md5($row['comment_content'] . $row['comment_author'] . $row['comment_author_url']),
lib/wfSchema.php CHANGED
@@ -139,7 +139,7 @@ class wfSchema {
139
  private $prefix = 'wp_';
140
  public function __construct($dbhost = false, $dbuser = false, $dbpassword = false, $dbname = false){
141
  if($dbhost){ //for testing
142
- $this->db = new wfDB($dbhost, $dbuser, $dbpassword, $dbname);
143
  $this->prefix = 'wp_';
144
  } else {
145
  global $wpdb;
139
  private $prefix = 'wp_';
140
  public function __construct($dbhost = false, $dbuser = false, $dbpassword = false, $dbname = false){
141
  if($dbhost){ //for testing
142
+ $this->db = new wfDB(false, $dbhost, $dbuser, $dbpassword, $dbname);
143
  $this->prefix = 'wp_';
144
  } else {
145
  global $wpdb;
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: mmaunder
3
  Tags: anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence
4
  Requires at least: 3.3.1
5
  Tested up to: 3.3.2
6
- Stable tag: 1.3.1
7
 
8
  Wordfence is an enterprise firewall and anti-virus plugin for WordPress.
9
 
@@ -89,6 +89,11 @@ Yes! Simply visit the Options page, click on advanced options and enable or disa
89
  5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
90
 
91
  == Changelog ==
 
 
 
 
 
92
  = 1.3.1 =
93
  * Fixed a bug where if you have the plugin "secure-wordpress" installed, you can't do a Wordfence scan because it says you have the wrong version. This is because secure-wordpress trashes the $wp_version global variable to hide your version rather than using the filters provided by WordPress. So coded a workaround so that your Wordfence scans will work with that plugin installed.
94
 
3
  Tags: anti-virus, malware, firewall, antivirus, virus, google safe browsing, phishing, scrapers, hacking, wordfence
4
  Requires at least: 3.3.1
5
  Tested up to: 3.3.2
6
+ Stable tag: 1.3.2
7
 
8
  Wordfence is an enterprise firewall and anti-virus plugin for WordPress.
9
 
89
  5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
90
 
91
  == Changelog ==
92
+ = 1.3.2 =
93
+ * Reduced the number of database connections that Wordfence makes to one.
94
+ * Modified the memory efficient unbuffered queries we use to only use a single DB connection.
95
+ * Removed status updates during post and comment scans which prevents interference with unbuffered queries and makes the scans even faster.
96
+
97
  = 1.3.1 =
98
  * Fixed a bug where if you have the plugin "secure-wordpress" installed, you can't do a Wordfence scan because it says you have the wrong version. This is because secure-wordpress trashes the $wp_version global variable to hide your version rather than using the filters provided by WordPress. So coded a workaround so that your Wordfence scans will work with that plugin installed.
99
 
wordfence.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Wordfence
4
  Plugin URI: http://wordfence.com/
5
  Description: Anti-virus and Firewall for WordPress
6
  Author: Mark Maunder
7
- Version: 1.3.1
8
  Author URI: http://wordfence.com/
9
  */
10
  require_once('lib/wordfenceConstants.php');
4
  Plugin URI: http://wordfence.com/
5
  Description: Anti-virus and Firewall for WordPress
6
  Author: Mark Maunder
7
+ Version: 1.3.2
8
  Author URI: http://wordfence.com/
9
  */
10
  require_once('lib/wordfenceConstants.php');