Wordfence Security – Firewall & Malware Scan - Version 1.4.4

Version Description

  • WordPress Multi-site support added. Currently in Beta. Tested with subdomains, not subdirectories, but it should work great on both.
  • Main changes are moving menus to the Network Admin area, preventing individual blogs from enabling the plugin and dealing with database prefix issues.
Download this release

Release Info

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

Code changes from version 1.4.3 to 1.4.4

lib/dropAll.php CHANGED
@@ -1,8 +1,8 @@
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]))){ echo "Usage: {$argv[0]} <DB username> <DB password> <DB name>\n"; exit(); } $s = new wfSchema('localhost', $argv[1], $argv[2], $argv[3]);
5
 
6
- $s->dropAll();
7
 
8
  ?>
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/wfConfig.php CHANGED
@@ -411,7 +411,7 @@ class wfConfig {
411
  private static function table(){
412
  if(! self::$table){
413
  global $wpdb;
414
- self::$table = $wpdb->prefix . 'wfConfig';
415
  }
416
  return self::$table;
417
  }
411
  private static function table(){
412
  if(! self::$table){
413
  global $wpdb;
414
+ self::$table = $wpdb->base_prefix . 'wfConfig';
415
  }
416
  return self::$table;
417
  }
lib/wfCrawl.php CHANGED
@@ -10,7 +10,7 @@ class wfCrawl {
10
  return false;
11
  }
12
  public static function verifyCrawlerPTR($hostPattern, $IP){
13
- global $wpdb; $table = $wpdb->prefix . 'wfCrawlers';
14
  $db = new wfDB();
15
  $IPn = wfUtils::inet_aton($IP);
16
  $status = $db->querySingle("select status from $table where IP=%s and patternSig=UNHEX(MD5('%s')) and lastUpdate > unix_timestamp() - %d", $IPn, $hostPattern, WORDFENCE_CRAWLER_VERIFY_CACHE_TIME);
10
  return false;
11
  }
12
  public static function verifyCrawlerPTR($hostPattern, $IP){
13
+ global $wpdb; $table = $wpdb->base_prefix . 'wfCrawlers';
14
  $db = new wfDB();
15
  $IPn = wfUtils::inet_aton($IP);
16
  $status = $db->querySingle("select status from $table where IP=%s and patternSig=UNHEX(MD5('%s')) and lastUpdate > unix_timestamp() - %d", $IPn, $hostPattern, WORDFENCE_CRAWLER_VERIFY_CACHE_TIME);
lib/wfIssues.php CHANGED
@@ -10,7 +10,7 @@ class wfIssues {
10
  public $totalWarningIssues = 0;
11
  public function __construct(){
12
  global $wpdb;
13
- $this->issuesTable = $wpdb->prefix . 'wfIssues';
14
  }
15
  public function addIssue($type, $severity,
16
 
10
  public $totalWarningIssues = 0;
11
  public function __construct(){
12
  global $wpdb;
13
+ $this->issuesTable = $wpdb->base_prefix . 'wfIssues';
14
  }
15
  public function addIssue($type, $severity,
16
 
lib/wfLog.php CHANGED
@@ -14,17 +14,17 @@ class wfLog {
14
  $this->apiKey = $apiKey;
15
  $this->wp_version = $wp_version;
16
  global $wpdb;
17
- $this->hitsTable = $wpdb->prefix . 'wfHits';
18
- $this->loginsTable = $wpdb->prefix . 'wfLogins';
19
- $this->locsTable = $wpdb->prefix . 'wfLocs';
20
- $this->blocksTable = $wpdb->prefix . 'wfBlocks';
21
- $this->lockOutTable = $wpdb->prefix . 'wfLockedOut';
22
- $this->leechTable = $wpdb->prefix . 'wfLeechers';
23
- $this->badLeechersTable = $wpdb->prefix . 'wfBadLeechers';
24
- $this->scanTable = $wpdb->prefix . 'wfScanners';
25
- $this->reverseTable = $wpdb->prefix . 'wfReverseCache';
26
- $this->throttleTable = $wpdb->prefix . 'wfThrottleLog';
27
- $this->statusTable = $wpdb->prefix . 'wfStatus';
28
  }
29
  public function logLogin($action, $fail, $username){
30
  $user = get_user_by('login', $username);
@@ -75,7 +75,7 @@ class wfLog {
75
  $this->takeBlockingAction('maxGlobalRequests', "Exceeded the maximum global requests per minute for crawlers or humans.");
76
  }
77
  if($type == '404'){
78
- global $wpdb; $p = $wpdb->prefix;
79
  if(wfConfig::get('other_WFNet')){
80
  $this->getDB()->query("insert IGNORE into $p"."wfNet404s (sig, ctime, URI) values (UNHEX(MD5('%s')), unix_timestamp(), '%s')", $_SERVER['REQUEST_URI'], $_SERVER['REQUEST_URI']);
81
  }
14
  $this->apiKey = $apiKey;
15
  $this->wp_version = $wp_version;
16
  global $wpdb;
17
+ $this->hitsTable = $wpdb->base_prefix . 'wfHits';
18
+ $this->loginsTable = $wpdb->base_prefix . 'wfLogins';
19
+ $this->locsTable = $wpdb->base_prefix . 'wfLocs';
20
+ $this->blocksTable = $wpdb->base_prefix . 'wfBlocks';
21
+ $this->lockOutTable = $wpdb->base_prefix . 'wfLockedOut';
22
+ $this->leechTable = $wpdb->base_prefix . 'wfLeechers';
23
+ $this->badLeechersTable = $wpdb->base_prefix . 'wfBadLeechers';
24
+ $this->scanTable = $wpdb->base_prefix . 'wfScanners';
25
+ $this->reverseTable = $wpdb->base_prefix . 'wfReverseCache';
26
+ $this->throttleTable = $wpdb->base_prefix . 'wfThrottleLog';
27
+ $this->statusTable = $wpdb->base_prefix . 'wfStatus';
28
  }
29
  public function logLogin($action, $fail, $username){
30
  $user = get_user_by('login', $username);
75
  $this->takeBlockingAction('maxGlobalRequests', "Exceeded the maximum global requests per minute for crawlers or humans.");
76
  }
77
  if($type == '404'){
78
+ global $wpdb; $p = $wpdb->base_prefix;
79
  if(wfConfig::get('other_WFNet')){
80
  $this->getDB()->query("insert IGNORE into $p"."wfNet404s (sig, ctime, URI) values (UNHEX(MD5('%s')), unix_timestamp(), '%s')", $_SERVER['REQUEST_URI'], $_SERVER['REQUEST_URI']);
81
  }
lib/wfModTracker.php CHANGED
@@ -9,7 +9,7 @@ class wfModTracker {
9
  private $anyFilesChangedCached = false;
10
  public function __construct(){
11
  global $wpdb;
12
- $this->changesTable = $wpdb->prefix . 'wfFileChanges';
13
  $this->status(2, 'info', "Getting file change DB handle");
14
  $this->db = new wfDB();
15
  $this->status(2, 'info', "Starting theme change check");
@@ -29,7 +29,7 @@ class wfModTracker {
29
  wfConfig::set('wfmdt_pluginSum', '');
30
  $db = new wfDB();
31
  global $wpdb;
32
- $db->query("delete from " . $wpdb->prefix . 'wfFileChanges');
33
  }
34
  public function filesModifiedInCore(){ if(wfConfig::get('wfmdt_coreSum') != $this->coreSum){ return true; } else { return false; } }
35
  public function filesModifiedInThemes(){ if(wfConfig::get('wfmdt_themeSum') != $this->themeSum){ return true; } else { return false; } }
9
  private $anyFilesChangedCached = false;
10
  public function __construct(){
11
  global $wpdb;
12
+ $this->changesTable = $wpdb->base_prefix . 'wfFileChanges';
13
  $this->status(2, 'info', "Getting file change DB handle");
14
  $this->db = new wfDB();
15
  $this->status(2, 'info', "Starting theme change check");
29
  wfConfig::set('wfmdt_pluginSum', '');
30
  $db = new wfDB();
31
  global $wpdb;
32
+ $db->query("delete from " . $wpdb->base_prefix . 'wfFileChanges');
33
  }
34
  public function filesModifiedInCore(){ if(wfConfig::get('wfmdt_coreSum') != $this->coreSum){ return true; } else { return false; } }
35
  public function filesModifiedInThemes(){ if(wfConfig::get('wfmdt_themeSum') != $this->themeSum){ return true; } else { return false; } }
lib/wfSchema.php CHANGED
@@ -144,12 +144,12 @@ class wfSchema {
144
  } else {
145
  global $wpdb;
146
  $this->db = new wfDB();
147
- $this->prefix = $wpdb->prefix;
148
  }
149
  }
150
- public function dropAll(){
151
  foreach($this->tables as $table => $def){
152
- $this->db->query("drop table if exists " . $this->prefix . $table);
153
  }
154
  }
155
  public function createAll(){
144
  } else {
145
  global $wpdb;
146
  $this->db = new wfDB();
147
+ $this->prefix = $wpdb->base_prefix;
148
  }
149
  }
150
+ public function dropAll($prefix){
151
  foreach($this->tables as $table => $def){
152
+ $this->db->query("drop table if exists " . $prefix . $table);
153
  }
154
  }
155
  public function createAll(){
lib/wfUtils.php CHANGED
@@ -143,6 +143,12 @@ class wfUtils {
143
  return $wp_version;
144
  }
145
  }
 
 
 
 
 
 
146
  }
147
 
148
 
143
  return $wp_version;
144
  }
145
  }
146
+ public static function isAdminPageMU(){
147
+ if(preg_match('/^[\/a-zA-Z0-9\-\_\s\+\~\!\^\.]*\/wp-admin\/network\//', $_SERVER['REQUEST_URI'])){
148
+ return true;
149
+ }
150
+ return false;
151
+ }
152
  }
153
 
154
 
lib/wordfenceClass.php CHANGED
@@ -23,6 +23,9 @@ class wordfence {
23
  private static $wfLog = false;
24
  private static $hitID = 0;
25
  public static function installPlugin(){
 
 
 
26
  $schema = new wfSchema();
27
  $schema->createAll(); //if not exists
28
  wfConfig::setDefaults(); //If not set
@@ -32,10 +35,13 @@ class wordfence {
32
  if( !wp_next_scheduled( 'wordfence_hourly_cron' )){
33
  wp_schedule_event(time(), 'hourly', 'wordfence_daily_cron');
34
  }
35
-
 
 
 
36
  }
37
  public static function hourlyCron(){
38
- global $wpdb; $p = $wpdb->prefix;
39
  $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
40
  $patData = $api->call('get_known_vuln_pattern');
41
  if(is_array($patData) && $patData['pat']){
@@ -85,7 +91,7 @@ class wordfence {
85
  }
86
  public static function dailyCron(){
87
  $wfdb = new wfDB();
88
- global $wpdb; $p = $wpdb->prefix;
89
  $wfdb->query("delete from $p"."wfLocs where ctime < unix_timestamp() - %d", WORDFENCE_MAX_IPLOC_AGE);
90
  $wfdb->query("truncate table $p"."wfBadLeechers"); //only uses date that's less than 1 minute old
91
  $wfdb->query("delete from $p"."wfBlocks where blockedTime + %s < unix_timestamp()", wfConfig::get('blockedTime'));
@@ -136,6 +142,10 @@ class wordfence {
136
 
137
  }
138
  public static function install_actions(){
 
 
 
 
139
  add_action('wordfence_daily_cron', 'wordfence::dailyCron');
140
  add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
141
  add_action('plugins_loaded', 'wordfence::veryFirstAction');
@@ -160,11 +170,15 @@ class wordfence {
160
  add_filter('get_the_generator_rdf', 'wordfence::genFilter', 99, 2);
161
  add_filter('get_the_generator_comment', 'wordfence::genFilter', 99, 2);
162
  add_filter('get_the_generator_export', 'wordfence::genFilter', 99, 2);
163
-
164
  if(is_admin()){
165
- //both functions check if user is admin and we can't do that check now because user object doesn't exist.
166
  add_action('admin_init', 'wordfence::admin_init');
167
- add_action('admin_menu', 'wordfence::admin_menus');
 
 
 
 
 
 
168
  }
169
  }
170
  public static function ajaxReceiver(){
@@ -344,7 +358,7 @@ class wordfence {
344
  $content = "SITE: " . site_url() . "\nWP VERSION: " . wfUtils::getWPVersion() . "\nAPI KEY: " . wfConfig::get('apiKey') . "\nADMIN EMAIL: " . get_option('admin_email') . "\nLOG:\n\n";
345
  $wfdb = new wfDB();
346
  global $wpdb;
347
- $p = $wpdb->prefix;
348
  $q = $wfdb->query("select ctime, level, type, msg from $p"."wfStatus order by ctime desc limit 10000");
349
  while($r = mysql_fetch_assoc($q)){
350
  if($r['type'] == 'error'){
@@ -447,7 +461,7 @@ class wordfence {
447
  if(! $opts['other_WFNet']){
448
  $wfdb = new wfDB();
449
  global $wpdb;
450
- $p = $wpdb->prefix;
451
  $wfdb->query("delete from $p"."wfBlocks where wfsn=1");
452
  }
453
  foreach($opts as $key => $val){
@@ -559,7 +573,7 @@ class wordfence {
559
  public static function ajax_ticker_callback(){
560
  $wfdb = new wfDB();
561
  global $wpdb;
562
- $p = $wpdb->prefix;
563
 
564
  $serverTime = $wfdb->querySingle("select unix_timestamp()");
565
  $issues = new wfIssues();
@@ -1012,12 +1026,16 @@ class wordfence {
1012
  require 'menu_scan.php';
1013
  }
1014
  public static function isAdmin(){
1015
- foreach(array('update_core', 'activate_plugins', 'add_users', 'create_users', 'create_users', 'install_themes') as $capability){
1016
- if(! current_user_can($capability)){
1017
- return false;
 
 
 
 
1018
  }
1019
  }
1020
- return true;
1021
  }
1022
  public static function status($level /* 1 has highest visibility */, $type /* info|error */, $msg){
1023
  if($type != 'info' && $type != 'error'){ error_log("Invalid status type: $type"); return; }
23
  private static $wfLog = false;
24
  private static $hitID = 0;
25
  public static function installPlugin(){
26
+ if(is_multisite() && @$_GET['networkwide'] != 1){
27
+ die("Sorry but you can't activate Wordfence on an individual site when WordPress MultiSite is enabled. Only the Network Admin can enable Wordfence and only they have access to administer Wordfence.");
28
+ }
29
  $schema = new wfSchema();
30
  $schema->createAll(); //if not exists
31
  wfConfig::setDefaults(); //If not set
35
  if( !wp_next_scheduled( 'wordfence_hourly_cron' )){
36
  wp_schedule_event(time(), 'hourly', 'wordfence_daily_cron');
37
  }
38
+ update_option('wordfenceActivated', 1);
39
+ }
40
+ public static function uninstallPlugin(){
41
+ update_option('wordfenceActivated', 0);
42
  }
43
  public static function hourlyCron(){
44
+ global $wpdb; $p = $wpdb->base_prefix;
45
  $api = new wfAPI(wfConfig::get('apiKey'), wfUtils::getWPVersion());
46
  $patData = $api->call('get_known_vuln_pattern');
47
  if(is_array($patData) && $patData['pat']){
91
  }
92
  public static function dailyCron(){
93
  $wfdb = new wfDB();
94
+ global $wpdb; $p = $wpdb->base_prefix;
95
  $wfdb->query("delete from $p"."wfLocs where ctime < unix_timestamp() - %d", WORDFENCE_MAX_IPLOC_AGE);
96
  $wfdb->query("truncate table $p"."wfBadLeechers"); //only uses date that's less than 1 minute old
97
  $wfdb->query("delete from $p"."wfBlocks where blockedTime + %s < unix_timestamp()", wfConfig::get('blockedTime'));
142
 
143
  }
144
  public static function install_actions(){
145
+ if(defined('MULTISITE')){
146
+ global $blog_id;
147
+ 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?!)
148
+ }
149
  add_action('wordfence_daily_cron', 'wordfence::dailyCron');
150
  add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
151
  add_action('plugins_loaded', 'wordfence::veryFirstAction');
170
  add_filter('get_the_generator_rdf', 'wordfence::genFilter', 99, 2);
171
  add_filter('get_the_generator_comment', 'wordfence::genFilter', 99, 2);
172
  add_filter('get_the_generator_export', 'wordfence::genFilter', 99, 2);
 
173
  if(is_admin()){
 
174
  add_action('admin_init', 'wordfence::admin_init');
175
+ if(is_multisite()){
176
+ if(wfUtils::isAdminPageMU()){
177
+ add_action('network_admin_menu', 'wordfence::admin_menus');
178
+ } //else don't show menu
179
+ } else {
180
+ add_action('admin_menu', 'wordfence::admin_menus');
181
+ }
182
  }
183
  }
184
  public static function ajaxReceiver(){
358
  $content = "SITE: " . site_url() . "\nWP VERSION: " . wfUtils::getWPVersion() . "\nAPI KEY: " . wfConfig::get('apiKey') . "\nADMIN EMAIL: " . get_option('admin_email') . "\nLOG:\n\n";
359
  $wfdb = new wfDB();
360
  global $wpdb;
361
+ $p = $wpdb->base_prefix;
362
  $q = $wfdb->query("select ctime, level, type, msg from $p"."wfStatus order by ctime desc limit 10000");
363
  while($r = mysql_fetch_assoc($q)){
364
  if($r['type'] == 'error'){
461
  if(! $opts['other_WFNet']){
462
  $wfdb = new wfDB();
463
  global $wpdb;
464
+ $p = $wpdb->base_prefix;
465
  $wfdb->query("delete from $p"."wfBlocks where wfsn=1");
466
  }
467
  foreach($opts as $key => $val){
573
  public static function ajax_ticker_callback(){
574
  $wfdb = new wfDB();
575
  global $wpdb;
576
+ $p = $wpdb->base_prefix;
577
 
578
  $serverTime = $wfdb->querySingle("select unix_timestamp()");
579
  $issues = new wfIssues();
1026
  require 'menu_scan.php';
1027
  }
1028
  public static function isAdmin(){
1029
+ if(is_multisite()){
1030
+ if(current_user_can('manage_network')){
1031
+ return true;
1032
+ }
1033
+ } else {
1034
+ if(current_user_can('update_core')){
1035
+ return true;
1036
  }
1037
  }
1038
+ return false;
1039
  }
1040
  public static function status($level /* 1 has highest visibility */, $type /* info|error */, $msg){
1041
  if($type != 'info' && $type != 'error'){ error_log("Invalid status type: $type"); return; }
readme.txt CHANGED
@@ -3,7 +3,7 @@ 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: 1.4.3
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
 
@@ -11,6 +11,8 @@ Wordfence Security is a free enterprise class security plugin that includes a fi
11
 
12
  Wordfence Security is a free enterprise class security plugin that includes a firewall and anti-virus scanning for WordPress websites.
13
 
 
 
14
  [Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
15
 
16
  Wordfence is 100% free. You need to sign up on Wordfence.com to get a free API key.
@@ -19,6 +21,7 @@ We also offer a Premium API key that adds additional scanning capabilities. See
19
  Wordfence:
20
 
21
  * Scans core files against repository versions to check their integrity.
 
22
  * Premium API key also scans themes and plugins against repository versions. This is currently the only difference between free and premium API keys.
23
  * See how files have changed. Optionally repair changed files.
24
  * Scans for signatures of over 44,000 known malware variants that are known security threats.
@@ -54,6 +57,17 @@ To install Wordfence Security and start protecting your WordPress website:
54
  1. Optionally change your security level or click the advanced options link to see individual security scanning and protection options.
55
  1. Click the "Live Traffic" menu option to watch your site activity in real-time.
56
 
 
 
 
 
 
 
 
 
 
 
 
57
  == Frequently Asked Questions ==
58
 
59
  [Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
@@ -64,6 +78,10 @@ Wordfence securely contacts our servers when doing a security scan. These includ
64
  against the official versions to see if security has been compromised, checking if URL's in your comments, posts and files are on any known list of dangerous URL's and checking
65
  if any of your file signatures match a large list of known malware files that constitute a security threat.
66
 
 
 
 
 
67
  = Will Wordfence slow my site down? =
68
 
69
  We have spent a lot of time making sure Wordfence runs very quickly and securely. Wordfence uses its own database
@@ -134,6 +152,10 @@ or a theme, because often these have been updated to fix a security hole.
134
  5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
135
 
136
  == Changelog ==
 
 
 
 
137
  = 1.4.3 =
138
  * Improved diagnistic information on binary and regular API calls for better debugging.
139
  * Changed ticker to only show activity with level < 3
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: 1.4.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
 
11
 
12
  Wordfence Security is a free enterprise class security plugin that includes a firewall and anti-virus scanning for WordPress websites.
13
 
14
+ Wordfence is now Multi-Site compatible. Support for Multi-Site is currently in Beta. Please visit our forums to report any issues.
15
+
16
  [Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
17
 
18
  Wordfence is 100% free. You need to sign up on Wordfence.com to get a free API key.
21
  Wordfence:
22
 
23
  * Scans core files against repository versions to check their integrity.
24
+ * WordPress Multi-Site (or WordPress MU in the older parlance) compatible (beta).
25
  * Premium API key also scans themes and plugins against repository versions. This is currently the only difference between free and premium API keys.
26
  * See how files have changed. Optionally repair changed files.
27
  * Scans for signatures of over 44,000 known malware variants that are known security threats.
57
  1. Optionally change your security level or click the advanced options link to see individual security scanning and protection options.
58
  1. Click the "Live Traffic" menu option to watch your site activity in real-time.
59
 
60
+ To install Wordfence on WordPress Multi-Site installations (support is currently in Beta):
61
+
62
+ 1. Install Wordfence via the plugin directory or by uploading the ZIP file.
63
+ 1. Network Activate Wordfence. This step is important because until you network activate it, your sites will see the plugin option on their plugins menu. Once activated that option dissapears. If one of your users manages to sneak in and try to activate Wordfence between you installing Wordfence and network activating it, don't worry because they won't be allowed to activate the plugin. It will generate a warning and won't activate for an individual site.
64
+ 1. Now that Wordfence is network activated it will appear on your Network Admin menu. Wordfence will not appear on any individual site's menu.
65
+ 1. Enter your API key to start your first scan.
66
+ 1. Wordfence will scan all files in your WordPress installation including those in the blogs.dir directory of your individual sites.
67
+ 1. Live Traffic will appear for ALL sites in your network. If you have a heavily trafficed system you may want to disable live traffic which will stop logging to the DB.
68
+ 1. Firewall rules and login rules apply to the WHOLE system. So if you fail a login on site1.example.com and site2.example.com it counts as 2 failures. Crawler traffic is counted between blogs, so if you hit three sites in the network, all the hits are totalled and that counts as the rate you're accessing the system.
69
+ 1. Wordfence has been tested with subdomains, not with subdirectories yet, but it should work. Please report all bugs and we'll fix them as fast as we can.
70
+
71
  == Frequently Asked Questions ==
72
 
73
  [Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
78
  against the official versions to see if security has been compromised, checking if URL's in your comments, posts and files are on any known list of dangerous URL's and checking
79
  if any of your file signatures match a large list of known malware files that constitute a security threat.
80
 
81
+ = Does Wordfence support Multi-Site installations? =
82
+
83
+ Yes. WordPress MU or Multi-Site as it's called now is supported and support is currently in beta. See the installation tab for more info.
84
+
85
  = Will Wordfence slow my site down? =
86
 
87
  We have spent a lot of time making sure Wordfence runs very quickly and securely. Wordfence uses its own database
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
+ = 1.4.4 =
156
+ * WordPress Multi-site support added. Currently in Beta. Tested with subdomains, not subdirectories, but it should work great on both.
157
+ * Main changes are moving menus to the Network Admin area, preventing individual blogs from enabling the plugin and dealing with database prefix issues.
158
+
159
  = 1.4.3 =
160
  * Improved diagnistic information on binary and regular API calls for better debugging.
161
  * Changed ticker to only show activity with level < 3
visitor.php CHANGED
@@ -22,7 +22,7 @@ function wfVisitor(){
22
  $hid = wfUtils::decrypt($hid);
23
  if(! preg_match('/^\d+$/', $hid)){ exit(); }
24
  $db = new wfDB();
25
- global $wpdb; $p = $wpdb->prefix;
26
  $db->query("update $p"."wfHits set jsRun=1 where id=%d", $hid);
27
  exit();
28
  }
22
  $hid = wfUtils::decrypt($hid);
23
  if(! preg_match('/^\d+$/', $hid)){ exit(); }
24
  $db = new wfDB();
25
+ global $wpdb; $p = $wpdb->base_prefix;
26
  $db->query("update $p"."wfHits set jsRun=1 where id=%d", $hid);
27
  exit();
28
  }
wordfence.php CHANGED
@@ -4,12 +4,13 @@ Plugin Name: Wordfence Security
4
  Plugin URI: http://wordfence.com/
5
  Description: WordPress Security - Anti-virus and Firewall security plugin for WordPress
6
  Author: Mark Maunder
7
- Version: 1.4.3
8
  Author URI: http://wordfence.com/
9
  */
10
  require_once('lib/wordfenceConstants.php');
11
  require_once('lib/wordfenceClass.php');
12
  register_activation_hook(WP_PLUGIN_DIR . '/wordfence/wordfence.php', 'wordfence::installPlugin');
 
13
  wordfence::install_actions();
14
 
15
 
4
  Plugin URI: http://wordfence.com/
5
  Description: WordPress Security - Anti-virus and Firewall security plugin for WordPress
6
  Author: Mark Maunder
7
+ Version: 1.4.4
8
  Author URI: http://wordfence.com/
9
  */
10
  require_once('lib/wordfenceConstants.php');
11
  require_once('lib/wordfenceClass.php');
12
  register_activation_hook(WP_PLUGIN_DIR . '/wordfence/wordfence.php', 'wordfence::installPlugin');
13
+ register_deactivation_hook(WP_PLUGIN_DIR . '/wordfence/wordfence.php', 'wordfence::uninstallPlugin');
14
  wordfence::install_actions();
15
 
16