Version Description
Download this release
Release Info
Developer | mmaunder |
Plugin | Wordfence Security – Firewall & Malware Scan |
Version | v1.4.1 |
Comparing to | |
See all releases |
Code changes from version 1.3.3 to v1.4.1
- lib/wfAPI.php +5 -2
- lib/wfIssues.php +0 -10
- lib/wfModTracker.php +10 -0
- lib/wfScanEngine.php +29 -83
- lib/wordfenceConstants.php +1 -1
- lib/wordfenceHash.php +55 -45
- readme.txt +20 -4
- wordfence.php +2 -2
lib/wfAPI.php
CHANGED
@@ -56,7 +56,8 @@ class wfAPI {
|
|
56 |
curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence.com UA " . WORDFENCE_VERSION);
|
57 |
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
58 |
curl_setopt ($curl, CURLOPT_HEADER, 0);
|
59 |
-
curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER,
|
|
|
60 |
curl_setopt ($curl, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
|
61 |
@curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
|
62 |
@curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
|
@@ -94,7 +95,9 @@ class wfAPI {
|
|
94 |
//curl_setopt($curl, CURLOPT_VERBOSE, true);
|
95 |
curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence");
|
96 |
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
97 |
-
curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER,
|
|
|
|
|
98 |
@curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
|
99 |
@curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
|
100 |
curl_setopt($curl, CURLOPT_POST, true);
|
56 |
curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence.com UA " . WORDFENCE_VERSION);
|
57 |
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
58 |
curl_setopt ($curl, CURLOPT_HEADER, 0);
|
59 |
+
curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, false);
|
60 |
+
curl_setopt ($curl, CURLOPT_SSL_VERIFYHOST, false);
|
61 |
curl_setopt ($curl, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
|
62 |
@curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
|
63 |
@curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
|
95 |
//curl_setopt($curl, CURLOPT_VERBOSE, true);
|
96 |
curl_setopt ($curl, CURLOPT_USERAGENT, "Wordfence");
|
97 |
curl_setopt ($curl, CURLOPT_RETURNTRANSFER, TRUE);
|
98 |
+
curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, false);
|
99 |
+
curl_setopt ($curl, CURLOPT_SSL_VERIFYHOST, false);
|
100 |
+
|
101 |
@curl_setopt ($curl, CURLOPT_FOLLOWLOCATION, true);
|
102 |
@curl_setopt ($curl, CURLOPT_MAXREDIRS, 10);
|
103 |
curl_setopt($curl, CURLOPT_POST, true);
|
lib/wfIssues.php
CHANGED
@@ -29,10 +29,6 @@ class wfIssues {
|
|
29 |
if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return; }
|
30 |
}
|
31 |
|
32 |
-
//This is flagged either during a scan or after a scan. The next scan checks to see if it's been set and will do a full scan if it's set. The next scan also sets this to zero.
|
33 |
-
//So the logic is: If any new issues have been added in the previous scan or since then, then do a full scan because we delete the 'new' issues at the start of each scan and need to check if they still exist.
|
34 |
-
wfConfig::set('newIssueAddedLastScan', 1);
|
35 |
-
|
36 |
$this->totalIssues++;
|
37 |
if($severity == 1){
|
38 |
$this->totalCriticalIssues++;
|
@@ -61,14 +57,12 @@ class wfIssues {
|
|
61 |
);
|
62 |
}
|
63 |
public function deleteIgnored(){
|
64 |
-
wfConfig::set('ignoreListChanged', 1);
|
65 |
$this->getDB()->query("delete from " . $this->issuesTable . " where status='ignoreP' or status='ignoreC'");
|
66 |
}
|
67 |
public function deleteNew(){
|
68 |
$this->getDB()->query("delete from " . $this->issuesTable . " where status='new'");
|
69 |
}
|
70 |
public function ignoreAllNew(){
|
71 |
-
wfConfig::set('ignoreListChanged', 1);
|
72 |
$this->getDB()->query("update " . $this->issuesTable . " set status='ignoreC' where status='new'");
|
73 |
}
|
74 |
public function emailNewIssues(){
|
@@ -122,10 +116,6 @@ class wfIssues {
|
|
122 |
}
|
123 |
public function updateIssue($id, $status){ //ignoreC, ignoreP, delete or new
|
124 |
$currentStatus = $this->getDB()->querySingle("select status from " . $this->issuesTable . " where id=%d", $id);
|
125 |
-
if($currentStatus == 'ignoreC' || $currentStatus == 'ignoreP'){
|
126 |
-
//We are removing something from the ignore list so we need to flag ignoreListChanged so that the next scan is a full scan
|
127 |
-
wfConfig::set('ignoreListChanged', 1);
|
128 |
-
}
|
129 |
if($status == 'delete'){
|
130 |
$this->getDB()->query("delete from " . $this->issuesTable . " where id=%d", $id);
|
131 |
} else if($status == 'ignoreC' || $status == 'ignoreP' || $status == 'new'){
|
29 |
if($rec['status'] == 'ignoreP' && $rec['ignoreP'] == $ignoreP){ return; }
|
30 |
}
|
31 |
|
|
|
|
|
|
|
|
|
32 |
$this->totalIssues++;
|
33 |
if($severity == 1){
|
34 |
$this->totalCriticalIssues++;
|
57 |
);
|
58 |
}
|
59 |
public function deleteIgnored(){
|
|
|
60 |
$this->getDB()->query("delete from " . $this->issuesTable . " where status='ignoreP' or status='ignoreC'");
|
61 |
}
|
62 |
public function deleteNew(){
|
63 |
$this->getDB()->query("delete from " . $this->issuesTable . " where status='new'");
|
64 |
}
|
65 |
public function ignoreAllNew(){
|
|
|
66 |
$this->getDB()->query("update " . $this->issuesTable . " set status='ignoreC' where status='new'");
|
67 |
}
|
68 |
public function emailNewIssues(){
|
116 |
}
|
117 |
public function updateIssue($id, $status){ //ignoreC, ignoreP, delete or new
|
118 |
$currentStatus = $this->getDB()->querySingle("select status from " . $this->issuesTable . " where id=%d", $id);
|
|
|
|
|
|
|
|
|
119 |
if($status == 'delete'){
|
120 |
$this->getDB()->query("delete from " . $this->issuesTable . " where id=%d", $id);
|
121 |
} else if($status == 'ignoreC' || $status == 'ignoreP' || $status == 'new'){
|
lib/wfModTracker.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
class wfModTracker {
|
3 |
private $themeSum = false;
|
4 |
private $pluginSum = false;
|
@@ -9,12 +10,18 @@ class wfModTracker {
|
|
9 |
public function __construct(){
|
10 |
global $wpdb;
|
11 |
$this->changesTable = $wpdb->prefix . 'wfFileChanges';
|
|
|
12 |
$this->db = new wfDB();
|
|
|
13 |
$this->themeSum = $this->makeSum(get_theme_root());
|
|
|
14 |
$this->pluginSum = $this->makeSum(WP_PLUGIN_DIR);
|
|
|
15 |
$this->coreSum = $this->makeCoreSum();
|
16 |
$this->allFilesSum = array();
|
|
|
17 |
$this->getAllFilesSum(ABSPATH);
|
|
|
18 |
}
|
19 |
public static function resetChanges(){
|
20 |
wfConfig::set('wfmdt_coreSum', '');
|
@@ -111,5 +118,8 @@ class wfModTracker {
|
|
111 |
}
|
112 |
return md5($str);
|
113 |
}
|
|
|
|
|
|
|
114 |
}
|
115 |
?>
|
1 |
<?php
|
2 |
+
require_once('wordfenceClass.php');
|
3 |
class wfModTracker {
|
4 |
private $themeSum = false;
|
5 |
private $pluginSum = 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");
|
16 |
$this->themeSum = $this->makeSum(get_theme_root());
|
17 |
+
$this->status(2, 'info', "Starting plugin change scan");
|
18 |
$this->pluginSum = $this->makeSum(WP_PLUGIN_DIR);
|
19 |
+
$this->status(2, 'info', "Starting core file change scan");
|
20 |
$this->coreSum = $this->makeCoreSum();
|
21 |
$this->allFilesSum = array();
|
22 |
+
$this->status(2, 'info', "Getting changes in all other files");
|
23 |
$this->getAllFilesSum(ABSPATH);
|
24 |
+
$this->status(2, 'info', "Done compiling file changes");
|
25 |
}
|
26 |
public static function resetChanges(){
|
27 |
wfConfig::set('wfmdt_coreSum', '');
|
118 |
}
|
119 |
return md5($str);
|
120 |
}
|
121 |
+
private function status($level, $type, $msg){
|
122 |
+
wordfence::status($level, $type, $msg);
|
123 |
+
}
|
124 |
}
|
125 |
?>
|
lib/wfScanEngine.php
CHANGED
@@ -6,7 +6,6 @@ require_once('wordfenceScanner.php');
|
|
6 |
require_once('wfIssues.php');
|
7 |
require_once('wfDB.php');
|
8 |
require_once('wfUtils.php');
|
9 |
-
require_once('wfModTracker.php');
|
10 |
class wfScanEngine {
|
11 |
private $i = false;
|
12 |
private $api = false;
|
@@ -14,7 +13,6 @@ class wfScanEngine {
|
|
14 |
private $wp_version = false;
|
15 |
private $apiKey = false;
|
16 |
private $errorStopped = false;
|
17 |
-
private $modTracker = false;
|
18 |
private $dictWords = array();
|
19 |
public function __construct(){
|
20 |
$this->i = new wfIssues();
|
@@ -26,52 +24,11 @@ class wfScanEngine {
|
|
26 |
}
|
27 |
public function go(){
|
28 |
$this->status(1, 'info', "Initializing scan");
|
29 |
-
|
30 |
-
/*
|
31 |
-
The logic for determining if we need to do a full scan or only scan files that have changed is as follows:
|
32 |
-
|
33 |
-
NOTE NOTE: We delete all 'new' issues at the start of every scan.
|
34 |
-
|
35 |
-
If the last scan added any new issues, or if new issues have been added since then, we do a full scan
|
36 |
-
because at the start of this scan we delete those 'new' issues and we need to check which of those new issues
|
37 |
-
are still issues.
|
38 |
-
|
39 |
-
If the previous scan didn't detect any new issues, no issues have been added since then and no files have changed
|
40 |
-
then we don't need to do a full scan UNLESS:
|
41 |
-
|
42 |
-
If the user has deleted 'ignored' issues then we need to do a full scan to see if any of those previously
|
43 |
-
ignored issues need to show up in the 'new' list.
|
44 |
-
|
45 |
-
So to accomplish this we track if new issues have been added during or since the previous scan and if any ignored issues
|
46 |
-
have been modified since the previous scan. If any of that is true, then we force a new scan.
|
47 |
-
|
48 |
-
NOTE that we don't have to 'resetChanges()' if the user deletes 'new' issues or moves them to 'ignore' because a full scan
|
49 |
-
will already occur because the previous scan called addIssue to create those issues (because we delete all new issues at the start of each scan)
|
50 |
-
and thereby set newIssueAddedLastScan.
|
51 |
-
|
52 |
-
*/
|
53 |
-
|
54 |
-
$forceFullScan = false;
|
55 |
-
if(wfConfig::get('newIssueAddedLastScan', false)){
|
56 |
-
$forceFullScan = true; //Do a full scan if a new issue was added in the last scan (or since the last scan) because we need to check if that issue has been resolved or still exists.
|
57 |
-
}
|
58 |
-
if(wfConfig::get('ignoreListChanged', false)){
|
59 |
-
$forceFullScan = true; //Do a full scan if the ignore list changed because we need to show that issue in 'new' list if it still exists.
|
60 |
-
}
|
61 |
-
wfConfig::set('newIssueAddedLastScan', 0);
|
62 |
-
wfConfig::set('ignoreListChanged', 0);
|
63 |
$this->i->deleteNew();
|
64 |
|
65 |
try {
|
66 |
-
if($forceFullScan){
|
67 |
-
$this->status(1, 'info', "Getting ready to do a full scan by reinitializing change tracking");
|
68 |
-
wfModTracker::resetChanges();
|
69 |
-
}
|
70 |
-
$this->status(1, 'info', "Compiling list of changed files");
|
71 |
-
$this->modTracker = new wfModTracker();
|
72 |
$this->doScan();
|
73 |
if(! $this->errorStopped){
|
74 |
-
$this->modTracker->logCurrentState();
|
75 |
wfConfig::set('lastScanCompleted', 'ok');
|
76 |
}
|
77 |
//updating this scan ID will trigger the scan page to load/reload the results.
|
@@ -79,7 +36,6 @@ class wfScanEngine {
|
|
79 |
//scan ID only incremented at end of scan to make UI load new results
|
80 |
$this->emailNewIssues();
|
81 |
} catch(Exception $e){
|
82 |
-
wfModTracker::resetChanges();
|
83 |
$this->errorStop($e->getMessage());
|
84 |
}
|
85 |
wordfence::scheduleNextScan(true);
|
@@ -94,15 +50,13 @@ class wfScanEngine {
|
|
94 |
$this->errorStop($this->api->errorMsg);
|
95 |
return;
|
96 |
}
|
97 |
-
$
|
98 |
if($this->errorStopped){
|
99 |
-
wfModTracker::resetChanges();
|
100 |
return;
|
101 |
}
|
102 |
if(wfConfig::get('scansEnabled_fileContents')){
|
103 |
-
$this->scanFileContents($
|
104 |
if($this->errorStopped){
|
105 |
-
wfModTracker::resetChanges();
|
106 |
return;
|
107 |
}
|
108 |
}
|
@@ -138,43 +92,23 @@ class wfScanEngine {
|
|
138 |
private function scanKnownFiles(){
|
139 |
$malwareScanEnabled = $coreScanEnabled = $pluginScanEnabled = $themeScanEnabled = false;
|
140 |
if(wfConfig::get('scansEnabled_core')){
|
141 |
-
|
142 |
-
$this->status(2, 'info', "Enabling core scan because core files have been modified.");
|
143 |
-
$coreScanEnabled = true;
|
144 |
-
} else {
|
145 |
-
$this->status(2, 'info', "Skipping core scan because no core files were modified.");
|
146 |
-
}
|
147 |
} else {
|
148 |
$this->status(2, 'info', "Skipping core scan because it's disabled.");
|
149 |
}
|
150 |
if(wfConfig::get('scansEnabled_plugins')){
|
151 |
-
|
152 |
-
$this->status(2, 'info', "Enabling plugin scan because files in plugins directory have been modified.");
|
153 |
-
$pluginScanEnabled = true;
|
154 |
-
} else {
|
155 |
-
$this->status(2, 'info', "Skipping plugin scan because no files in plugins directory have been modified.");
|
156 |
-
}
|
157 |
} else {
|
158 |
$this->status(2, 'info', "Skipping plugin scan because it's disabled.");
|
159 |
}
|
160 |
if(wfConfig::get('scansEnabled_themes')){
|
161 |
-
|
162 |
-
$this->status(2, 'info', "Enabling theme scan because files in themes dir have been modified.");
|
163 |
-
$themeScanEnabled = true;
|
164 |
-
} else {
|
165 |
-
$this->status(2, 'info', "Skipping theme scan because no files in themes directory have been modified.");
|
166 |
-
}
|
167 |
} else {
|
168 |
$this->status(2, 'info', "Skipping themes scan because it's disabled.");
|
169 |
}
|
170 |
|
171 |
if(wfConfig::get('scansEnabled_malware')){
|
172 |
-
|
173 |
-
$this->status(2, 'info', "Enabling malware scan because we have modified files in the dir structure.");
|
174 |
-
$malwareScanEnabled = true;
|
175 |
-
} else {
|
176 |
-
$this->status(2, 'info', "Skipping malware scan because no files were modified in the dir structure.");
|
177 |
-
}
|
178 |
} else {
|
179 |
$this->status(2, 'info', "Skipping malware scan because it's disabled.");
|
180 |
}
|
@@ -184,11 +118,25 @@ class wfScanEngine {
|
|
184 |
return array();
|
185 |
}
|
186 |
|
187 |
-
$this->status(1, 'info', "Scanning known files.");
|
188 |
//CORE SCAN
|
189 |
-
$this->status(2, 'info', "
|
190 |
-
$hasher = new wordfenceHash();
|
191 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
$this->status(2, 'info', "Done hash. Updating summary items.");
|
193 |
$this->i->updateSummaryItem('totalData', wfUtils::formatBytes($hasher->totalData));
|
194 |
$this->i->updateSummaryItem('totalFiles', $hasher->totalFiles);
|
@@ -256,19 +204,17 @@ class wfScanEngine {
|
|
256 |
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
|
257 |
$this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
|
258 |
}
|
259 |
-
return $result1['
|
260 |
}
|
261 |
-
private function scanFileContents($
|
262 |
$this->status(1, 'info', "Scanning file contents.");
|
263 |
-
if(! is_array($
|
264 |
-
$
|
265 |
}
|
266 |
$this->status(2, 'info', "Getting list of changed files since last scan.");
|
267 |
-
$filesToScan = $this->modTracker->getChangedFiles(ABSPATH, $knownFiles); //get changed files since last scan and exclude $knownFiles
|
268 |
-
$this->status(2, 'info', "Got " . sizeof($filesToScan) . " changed files to scan.");
|
269 |
$scanner = new wordfenceScanner($this->apiKey, $this->wp_version);
|
270 |
$this->status(2, 'info', "Starting scan of file contents");
|
271 |
-
$result2 = $scanner->scan(ABSPATH, $
|
272 |
$this->status(2, 'info', "Done file contents scan");
|
273 |
if($scanner->errorMsg){
|
274 |
$this->errorStop($scanner->errorMsg);
|
6 |
require_once('wfIssues.php');
|
7 |
require_once('wfDB.php');
|
8 |
require_once('wfUtils.php');
|
|
|
9 |
class wfScanEngine {
|
10 |
private $i = false;
|
11 |
private $api = false;
|
13 |
private $wp_version = false;
|
14 |
private $apiKey = false;
|
15 |
private $errorStopped = false;
|
|
|
16 |
private $dictWords = array();
|
17 |
public function __construct(){
|
18 |
$this->i = new wfIssues();
|
24 |
}
|
25 |
public function go(){
|
26 |
$this->status(1, 'info', "Initializing scan");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
$this->i->deleteNew();
|
28 |
|
29 |
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
$this->doScan();
|
31 |
if(! $this->errorStopped){
|
|
|
32 |
wfConfig::set('lastScanCompleted', 'ok');
|
33 |
}
|
34 |
//updating this scan ID will trigger the scan page to load/reload the results.
|
36 |
//scan ID only incremented at end of scan to make UI load new results
|
37 |
$this->emailNewIssues();
|
38 |
} catch(Exception $e){
|
|
|
39 |
$this->errorStop($e->getMessage());
|
40 |
}
|
41 |
wordfence::scheduleNextScan(true);
|
50 |
$this->errorStop($this->api->errorMsg);
|
51 |
return;
|
52 |
}
|
53 |
+
$unknownFiles = $this->scanKnownFiles();
|
54 |
if($this->errorStopped){
|
|
|
55 |
return;
|
56 |
}
|
57 |
if(wfConfig::get('scansEnabled_fileContents')){
|
58 |
+
$this->scanFileContents($unknownFiles);
|
59 |
if($this->errorStopped){
|
|
|
60 |
return;
|
61 |
}
|
62 |
}
|
92 |
private function scanKnownFiles(){
|
93 |
$malwareScanEnabled = $coreScanEnabled = $pluginScanEnabled = $themeScanEnabled = false;
|
94 |
if(wfConfig::get('scansEnabled_core')){
|
95 |
+
$coreScanEnabled = true;
|
|
|
|
|
|
|
|
|
|
|
96 |
} else {
|
97 |
$this->status(2, 'info', "Skipping core scan because it's disabled.");
|
98 |
}
|
99 |
if(wfConfig::get('scansEnabled_plugins')){
|
100 |
+
$pluginScanEnabled = true;
|
|
|
|
|
|
|
|
|
|
|
101 |
} else {
|
102 |
$this->status(2, 'info', "Skipping plugin scan because it's disabled.");
|
103 |
}
|
104 |
if(wfConfig::get('scansEnabled_themes')){
|
105 |
+
$themeScanEnabled = true;
|
|
|
|
|
|
|
|
|
|
|
106 |
} else {
|
107 |
$this->status(2, 'info', "Skipping themes scan because it's disabled.");
|
108 |
}
|
109 |
|
110 |
if(wfConfig::get('scansEnabled_malware')){
|
111 |
+
$malwareScanEnabled = true;
|
|
|
|
|
|
|
|
|
|
|
112 |
} else {
|
113 |
$this->status(2, 'info', "Skipping malware scan because it's disabled.");
|
114 |
}
|
118 |
return array();
|
119 |
}
|
120 |
|
|
|
121 |
//CORE SCAN
|
122 |
+
$this->status(2, 'info', "Examining files in WordPress base directory.");
|
123 |
+
$hasher = new wordfenceHash(strlen(ABSPATH));
|
124 |
+
$includeInScan = array( '.htaccess', 'index.php', 'license.txt', 'readme.html', 'wp-activate.php', 'wp-admin', 'wp-app.php', 'wp-blog-header.php', 'wp-comments-post.php', 'wp-config-sample.php', 'wp-content', 'wp-cron.php', 'wp-includes', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-pass.php', 'wp-register.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php');
|
125 |
+
$baseContents = scandir(ABSPATH);
|
126 |
+
$includeBase = true;
|
127 |
+
if(sizeof($baseContents) > 500){ //If there are more than 500 files in the base dir, then don't scan base dir files other than core WP files.
|
128 |
+
$includeBase = false;
|
129 |
+
}
|
130 |
+
if($includeBase){
|
131 |
+
foreach($baseContents as $file){ //Only include base files less than a meg that are files.
|
132 |
+
$file = rtrim(ABSPATH, '/') . '/' . $file;
|
133 |
+
if(is_file($file) && @filesize(ABSPATH . $file) < 1000000 && (! in_array($file, $includeInScan)) ){
|
134 |
+
$includeInScan[] = $file;
|
135 |
+
}
|
136 |
+
}
|
137 |
+
}
|
138 |
+
$this->status(2, 'info', "Hashing your WordPress files for comparison against originals.");
|
139 |
+
$hashes = $hasher->hashPaths(ABSPATH, $includeInScan);
|
140 |
$this->status(2, 'info', "Done hash. Updating summary items.");
|
141 |
$this->i->updateSummaryItem('totalData', wfUtils::formatBytes($hasher->totalData));
|
142 |
$this->i->updateSummaryItem('totalFiles', $hasher->totalFiles);
|
204 |
$this->status(2, 'info', "Adding issue: " . $issue['shortMsg']);
|
205 |
$this->addIssue($issue['type'], $issue['severity'], $issue['ignoreP'], $issue['ignoreC'], $issue['shortMsg'], $issue['longMsg'], $issue['data']);
|
206 |
}
|
207 |
+
return $result1['unknownFiles'];
|
208 |
}
|
209 |
+
private function scanFileContents($unknownFiles){
|
210 |
$this->status(1, 'info', "Scanning file contents.");
|
211 |
+
if(! is_array($unknownFiles)){
|
212 |
+
$unknownFiles = array();
|
213 |
}
|
214 |
$this->status(2, 'info', "Getting list of changed files since last scan.");
|
|
|
|
|
215 |
$scanner = new wordfenceScanner($this->apiKey, $this->wp_version);
|
216 |
$this->status(2, 'info', "Starting scan of file contents");
|
217 |
+
$result2 = $scanner->scan(ABSPATH, $unknownFiles, array($this, 'status'));
|
218 |
$this->status(2, 'info', "Done file contents scan");
|
219 |
if($scanner->errorMsg){
|
220 |
$this->errorStop($scanner->errorMsg);
|
lib/wordfenceConstants.php
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<?php
|
2 |
-
define('WORDFENCE_VERSION', 1.
|
3 |
define('WORDFENCE_API_URL', 'https://noc1.wordfence.com/');
|
4 |
define('WORDFENCE_MAX_SCAN_TIME', 3600);
|
5 |
define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
|
1 |
<?php
|
2 |
+
define('WORDFENCE_VERSION', 1.2);
|
3 |
define('WORDFENCE_API_URL', 'https://noc1.wordfence.com/');
|
4 |
define('WORDFENCE_MAX_SCAN_TIME', 3600);
|
5 |
define('WORDFENCE_TRANSIENTS_TIMEOUT', 3600); //how long are items cached in seconds e.g. files downloaded for diffing
|
lib/wordfenceHash.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
class wordfenceHash {
|
3 |
private $whitespace = array("\n","\r","\t"," ");
|
4 |
public $totalData = 0; //To do a sanity check, don't use 'du' because it gets sparse files wrong and reports blocks used on disk. Use : find . -type f -ls | awk '{total += $7} END {print total}'
|
@@ -6,63 +7,71 @@ class wordfenceHash {
|
|
6 |
public $totalDirs = 0;
|
7 |
public $linesOfPHP = 0;
|
8 |
public $linesOfJCH = 0; //lines of HTML, CSS and javascript
|
9 |
-
public
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
}
|
14 |
-
|
15 |
if($path[strlen($path) - 1] != '/'){
|
16 |
$path .= '/';
|
17 |
}
|
18 |
-
$
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
}
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
$
|
43 |
-
$dirHashes[substr($file, $striplen) . '/'] = $this->hashOfHashes($dirHashes);
|
44 |
-
$ret = array_merge($ret, $dirHashes);
|
45 |
}
|
46 |
}
|
47 |
-
return $ret;
|
48 |
}
|
49 |
-
|
50 |
-
|
51 |
-
$
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
58 |
}
|
59 |
-
return array( md5($all_md5, true), hash('sha256', $all_sha, true), hash('sha256', $all_shac, true) );
|
60 |
}
|
61 |
public function wfHash($file, $binary = true){
|
62 |
$md5 = @md5_file($file, $binary);
|
63 |
if(! $md5){ return false; }
|
64 |
-
|
65 |
-
if(! $sha){ return false; }
|
66 |
$fp = @fopen($file, "rb");
|
67 |
if(! $fp){
|
68 |
return false;
|
@@ -72,7 +81,8 @@ class wordfenceHash {
|
|
72 |
hash_update($ctx, str_replace($this->whitespace,"",fread($fp, 65536)));
|
73 |
}
|
74 |
$shac = hash_final($ctx, $binary);
|
75 |
-
|
|
|
76 |
}
|
77 |
public static function bin2hex($hashes){
|
78 |
function wf_func1($elem){
|
1 |
<?php
|
2 |
+
require_once('wordfenceClass.php');
|
3 |
class wordfenceHash {
|
4 |
private $whitespace = array("\n","\r","\t"," ");
|
5 |
public $totalData = 0; //To do a sanity check, don't use 'du' because it gets sparse files wrong and reports blocks used on disk. Use : find . -type f -ls | awk '{total += $7} END {print total}'
|
7 |
public $totalDirs = 0;
|
8 |
public $linesOfPHP = 0;
|
9 |
public $linesOfJCH = 0; //lines of HTML, CSS and javascript
|
10 |
+
public $striplen = 0;
|
11 |
+
private $hashes = array();
|
12 |
+
public function __construct($striplen){
|
13 |
+
$this->striplen = $striplen;
|
14 |
}
|
15 |
+
public function hashPaths($path, $only = array()){ //base path and 'only' is a list of files and dirs in the bast that are the only ones that should be processed. Everything else in base is ignored. If only is empty then everything is processed.
|
16 |
if($path[strlen($path) - 1] != '/'){
|
17 |
$path .= '/';
|
18 |
}
|
19 |
+
$files = scandir($path);
|
20 |
+
foreach($files as $file){
|
21 |
+
if(sizeof($only) > 0 && (! in_array($file, $only))){
|
22 |
+
continue;
|
23 |
+
}
|
24 |
+
$file = $path . $file;
|
25 |
+
wordfence::status(2, 'info', "Hashing item in base dir: $file");
|
26 |
+
$this->_dirHash($file);
|
27 |
+
}
|
28 |
+
return $this->hashes;
|
29 |
+
}
|
30 |
+
private function _dirHash($path){
|
31 |
+
if(substr($path, -3, 3) == '/..' || substr($path, -2, 2) == '/.'){
|
32 |
+
return;
|
33 |
+
}
|
34 |
+
if(is_dir($path)){
|
35 |
+
$this->totalDirs++;
|
36 |
+
if($path[strlen($path) - 1] != '/'){
|
37 |
+
$path .= '/';
|
38 |
+
}
|
39 |
+
$cont = scandir($path);
|
40 |
+
for($i = 0; $i < sizeof($cont); $i++){
|
41 |
+
if($cont[$i] == '.' || $cont[$i] == '..'){ continue; }
|
42 |
+
$file = $path . $cont[$i];
|
43 |
+
if(is_file($file)){
|
44 |
+
$this->processFile($file);
|
45 |
+
} else if(is_dir($file)) {
|
46 |
+
wordfence::status(2, 'info', "Traversing into dir $file");
|
47 |
+
$this->_dirHash($file);
|
48 |
}
|
49 |
+
}
|
50 |
+
} else {
|
51 |
+
if(is_file($path)){
|
52 |
+
$this->processFile($path);
|
|
|
|
|
53 |
}
|
54 |
}
|
|
|
55 |
}
|
56 |
+
private function processFile($file){
|
57 |
+
$wfHash = $this->wfHash($file, true);
|
58 |
+
if($wfHash){
|
59 |
+
$this->hashes[substr($file, $this->striplen)] = $wfHash;
|
60 |
+
//Now that we know we can open the file, lets update stats
|
61 |
+
if(preg_match('/\.(?:js|html|htm|css)$/i', $file)){
|
62 |
+
$this->linesOfJCH += sizeof(file($file));
|
63 |
+
} else if(preg_match('/\.php$/i', $file)){
|
64 |
+
$this->linesOfPHP += sizeof(file($file));
|
65 |
+
}
|
66 |
+
$this->totalFiles++;
|
67 |
+
$this->totalData += filesize($file);
|
68 |
}
|
|
|
69 |
}
|
70 |
public function wfHash($file, $binary = true){
|
71 |
$md5 = @md5_file($file, $binary);
|
72 |
if(! $md5){ return false; }
|
73 |
+
//$sha = @hash_file('sha256', $file, $binary);
|
74 |
+
//if(! $sha){ return false; }
|
75 |
$fp = @fopen($file, "rb");
|
76 |
if(! $fp){
|
77 |
return false;
|
81 |
hash_update($ctx, str_replace($this->whitespace,"",fread($fp, 65536)));
|
82 |
}
|
83 |
$shac = hash_final($ctx, $binary);
|
84 |
+
//Taking out $sha for now because we don't use it on the scanning server side
|
85 |
+
return array($md5, '', $shac, filesize($file) );
|
86 |
}
|
87 |
public static function bin2hex($hashes){
|
88 |
function wf_func1($elem){
|
readme.txt
CHANGED
@@ -1,15 +1,17 @@
|
|
1 |
=== Plugin Name ===
|
2 |
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.
|
7 |
|
8 |
-
Wordfence is
|
9 |
|
10 |
== Description ==
|
11 |
|
12 |
-
|
|
|
|
|
13 |
|
14 |
Wordfence is 100% free. You need to sign up on Wordfence.com to get a free API key.
|
15 |
We also offer a Premium API key that adds additional scanning capabilities. See below for details.
|
@@ -37,6 +39,8 @@ newest threats to your WordPress website.
|
|
37 |
|
38 |
== Installation ==
|
39 |
|
|
|
|
|
40 |
To install Wordfence and start protecting your WordPress website:
|
41 |
|
42 |
1. Install Wordfence automatically or by uploading the ZIP file.
|
@@ -51,6 +55,8 @@ To install Wordfence and start protecting your WordPress website:
|
|
51 |
|
52 |
== Frequently Asked Questions ==
|
53 |
|
|
|
|
|
54 |
= Why does Wordfence need an API key? =
|
55 |
|
56 |
Wordfence contacts our servers for a variety of reasons. These include: comparing the hashes of your core, theme and plugin files
|
@@ -89,6 +95,16 @@ 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.3 =
|
93 |
* Made real-time server polling more efficient.
|
94 |
* Entering your API key now automatically starts your first scan. Was causing some confusion.
|
1 |
=== Plugin Name ===
|
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: 1.4.1
|
7 |
|
8 |
+
Wordfence is a free enterprise class security plugin that includes a firewall and anti-virus scanning for WordPress websites.
|
9 |
|
10 |
== Description ==
|
11 |
|
12 |
+
[Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
|
13 |
+
|
14 |
+
Wordfence is a free enterprise class security plugin that includes a firewall and anti-virus scanning for WordPress websites.
|
15 |
|
16 |
Wordfence is 100% free. You need to sign up on Wordfence.com to get a free API key.
|
17 |
We also offer a Premium API key that adds additional scanning capabilities. See below for details.
|
39 |
|
40 |
== Installation ==
|
41 |
|
42 |
+
[Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
|
43 |
+
|
44 |
To install Wordfence and start protecting your WordPress website:
|
45 |
|
46 |
1. Install Wordfence automatically or by uploading the ZIP file.
|
55 |
|
56 |
== Frequently Asked Questions ==
|
57 |
|
58 |
+
[Remember to visit our support forums if you have questions or comments.](http://wordfence.com/forums/)
|
59 |
+
|
60 |
= Why does Wordfence need an API key? =
|
61 |
|
62 |
Wordfence contacts our servers for a variety of reasons. These include: comparing the hashes of your core, theme and plugin files
|
95 |
5. If you're technically minded, this is the under-the-hood view of Wordfence options where you can fine-tune your security settings.
|
96 |
|
97 |
== Changelog ==
|
98 |
+
= 1.4.1 =
|
99 |
+
* This is a major release, please upgrade immediately.
|
100 |
+
* Only scan files in the WordPress ABSPATH root directory and known WordPress subdirectories. Prevents potentially massive scans on hosts that have large dirs off their wordpress root.
|
101 |
+
* Don't generate plain SHA hashes anymore because we don't currently use them on the server side for scanning. (Still generates md5's and SHAC)
|
102 |
+
* No longer do change tracking on files before scans because the change tracking does almost the same amount of work when generating hashes as the actual scan. So just do the scan, which is now faster.
|
103 |
+
* Updated internal version to 1.2 to use new code on the server side which sends back a list of unknown files rather than known files, which is usually smaller and more network efficient.
|
104 |
+
* Improved logging in activity log.
|
105 |
+
* Removed SSL peer verification because some hosts have bad cert config. Connection to our servers is still via SSL.
|
106 |
+
* Fixed a few minor issues. Overall you should notice that scans are much faster now.
|
107 |
+
|
108 |
= 1.3.3 =
|
109 |
* Made real-time server polling more efficient.
|
110 |
* Entering your API key now automatically starts your first scan. Was causing some confusion.
|
wordfence.php
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
/*
|
3 |
Plugin Name: Wordfence
|
4 |
Plugin URI: http://wordfence.com/
|
5 |
-
Description: Anti-virus and Firewall for WordPress
|
6 |
Author: Mark Maunder
|
7 |
-
Version: 1.
|
8 |
Author URI: http://wordfence.com/
|
9 |
*/
|
10 |
require_once('lib/wordfenceConstants.php');
|
2 |
/*
|
3 |
Plugin Name: Wordfence
|
4 |
Plugin URI: http://wordfence.com/
|
5 |
+
Description: WordPress Security - Anti-virus and Firewall for WordPress
|
6 |
Author: Mark Maunder
|
7 |
+
Version: 1.4.1
|
8 |
Author URI: http://wordfence.com/
|
9 |
*/
|
10 |
require_once('lib/wordfenceConstants.php');
|