PluginProbe ʕ •ᴥ•ʔ
Wordfence Security – Firewall, Malware Scan, and Login Security / 6.1.15
Wordfence Security – Firewall, Malware Scan, and Login Security v6.1.15
8.2.2 8.2.1 8.2.0 3.7.1 3.7.2 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 3.8.9 3.9.1 4.0.1 4.0.2 4.0.3 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.0.9 5.1.1 5.1.2 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.2.1 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.1 5.3.10 5.3.11 5.3.12 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.3.8 5.3.9 6.0.1 6.0.10 6.0.11 6.0.12 6.0.14 6.0.15 6.0.16 6.0.17 6.0.18 6.0.19 6.0.2 6.0.20 6.0.21 6.0.22 6.0.23 6.0.24 6.0.25 6.0.3 6.0.4 6.0.5 6.0.6 6.0.7 6.0.8 6.0.9 6.1.1 6.1.10 6.1.11 6.1.12 6.1.14 6.1.15 6.1.16 6.1.17 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8 6.1.9 6.2.0 6.2.1 6.2.10 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.2.8 6.2.9 6.3.0 6.3.1 6.3.10 6.3.11 6.3.12 6.3.14 6.3.15 6.3.16 6.3.17 6.3.18 6.3.19 6.3.2 6.3.20 6.3.21 6.3.22 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.3.8 6.3.9 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1.0 7.1.1 7.1.10 7.1.11 7.1.12 7.1.14 7.1.15 7.1.16 7.1.17 7.1.18 7.1.19 7.1.2 7.1.20 7.1.3 7.1.4 7.1.5 7.1.6 7.1.7 7.1.8 7.1.9 7.10.0 7.10.1 7.10.2 7.10.3 7.10.4 7.10.5 7.10.6 7.10.7 7.11.0 7.11.1 7.11.2 7.11.3 7.11.4 7.11.5 7.11.6 7.11.7 7.2.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3.1 7.3.2 7.3.3 7.3.4 7.3.5 7.3.6 7.4.0 7.4.1 7.4.10 7.4.11 7.4.12 7.4.14 7.4.2 7.4.3 trunk 7.4.4 1.1 7.4.5 1.2 7.4.6 1.3 7.4.7 1.3.1 7.4.8 1.3.2 7.4.9 1.3.3 7.5.0 1.4.2 7.5.1 1.4.3 7.5.10 1.4.4 7.5.11 1.4.5 7.5.2 1.4.6 7.5.3 1.4.7 7.5.4 1.4.8 7.5.5 1.5.1 7.5.6 1.5.2 7.5.7 1.5.3 7.5.8 1.5.4 7.5.9 1.5.5 7.6.0 1.5.6 7.6.1 2.0.1 7.6.2 2.0.2 7.7.0 2.0.3 7.7.1 2.0.5 7.8.0 2.0.6 7.8.1 2.0.7 7.8.2 2.1.0 7.9.0 2.1.1 7.9.1 2.1.2 7.9.2 2.1.3 7.9.3 2.1.4 8.0.0 2.1.5 8.0.1 3.0.2 8.0.2 3.0.3 8.0.3 3.0.4 8.0.4 3.0.5 8.0.5 3.0.6 8.1.0 3.0.7 8.1.1 3.0.8 8.1.2 3.0.9 8.1.3 3.1.0 8.1.4 3.1.1 v1.4.1 3.1.2 3.1.4 3.1.6 3.2.1 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.1 3.4.4 3.4.5 3.5.1 3.5.2 3.6.1 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9
wordfence / lib / wfConfig.php
wordfence / lib Last commit date
Diff 11 years ago .htaccess 14 years ago Diff.php 14 years ago GeoIP.dat 9 years ago GeoIPv6.dat 9 years ago IPTraf.php 9 years ago compat.php 10 years ago conntest.php 11 years ago cronview.php 10 years ago dashboard.php 9 years ago dbview.php 11 years ago diffResult.php 11 years ago email_genericAlert.php 9 years ago email_newIssues.php 9 years ago email_passwdChanged.php 10 years ago email_pleaseChangePasswd.php 10 years ago email_unlockRequest.php 11 years ago menuHeader.php 12 years ago menu_activity.php 9 years ago menu_blockedIPs.php 9 years ago menu_countryBlocking.php 9 years ago menu_diagnostic.php 9 years ago menu_options.php 9 years ago menu_passwd.php 9 years ago menu_rangeBlocking.php 9 years ago menu_scan.php 9 years ago menu_scanSchedule.php 9 years ago menu_sitePerf.php 11 years ago menu_sitePerfStats.php 9 years ago menu_twoFactor.php 9 years ago menu_waf.php 9 years ago menu_whois.php 11 years ago pageTitle.php 11 years ago schedWeekEntry.php 12 years ago sysinfo.php 10 years ago unknownFiles.php 11 years ago viewFullActivityLog.php 10 years ago wf503.php 10 years ago wfAPI.php 9 years ago wfAction.php 14 years ago wfActivityReport.php 10 years ago wfArray.php 13 years ago wfBrowscap.php 12 years ago wfBrowscapCache.php 10 years ago wfBulkCountries.php 13 years ago wfCache.php 9 years ago wfConfig.php 9 years ago wfCountryMap.php 13 years ago wfCrawl.php 10 years ago wfCrypt.php 11 years ago wfDB.php 10 years ago wfDiagnostic.php 10 years ago wfDict.php 14 years ago wfDirectoryIterator.php 11 years ago wfGeoIP.php 9 years ago wfHelperBin.php 11 years ago wfHelperString.php 11 years ago wfIPWhitelist.php 9 years ago wfIssues.php 9 years ago wfLockedOut.php 13 years ago wfLog.php 9 years ago wfRate.php 10 years ago wfScan.php 9 years ago wfScanEngine.php 9 years ago wfSchema.php 10 years ago wfUnlockMsg.php 10 years ago wfUpdateCheck.php 9 years ago wfUtils.php 9 years ago wfView.php 10 years ago wfViewResult.php 11 years ago wordfenceClass.php 9 years ago wordfenceConstants.php 9 years ago wordfenceHash.php 9 years ago wordfenceScanner.php 9 years ago wordfenceURLHoover.php 9 years ago
wfConfig.php
725 lines
1 <?php
2 class wfConfig {
3 const AUTOLOAD = 'yes';
4 const DONT_AUTOLOAD = 'no';
5
6 public static $diskCache = array();
7 private static $diskCacheDisabled = false; //enables if we detect a write fail so we don't keep calling stat()
8 private static $cacheDisableCheckDone = false;
9 private static $table = false;
10 private static $tableExists = true;
11 private static $cache = array();
12 private static $DB = false;
13 private static $tmpFileHeader = "<?php\n/* Wordfence temporary file security header */\necho \"Nothing to see here!\\n\"; exit(0);\n?>";
14 private static $tmpDirCache = false;
15 public static $defaultConfig = array(
16 "checkboxes" => array(
17 "alertOn_critical" => array('value' => true, 'autoload' => self::AUTOLOAD),
18 "alertOn_update" => array('value' => false, 'autoload' => self::AUTOLOAD),
19 "alertOn_warnings" => array('value' => true, 'autoload' => self::AUTOLOAD),
20 "alertOn_throttle" => array('value' => false, 'autoload' => self::AUTOLOAD),
21 "alertOn_block" => array('value' => true, 'autoload' => self::AUTOLOAD),
22 "alertOn_loginLockout" => array('value' => true, 'autoload' => self::AUTOLOAD),
23 "alertOn_lostPasswdForm" => array('value' => true, 'autoload' => self::AUTOLOAD),
24 "alertOn_adminLogin" => array('value' => true, 'autoload' => self::AUTOLOAD),
25 "alertOn_nonAdminLogin" => array('value' => false, 'autoload' => self::AUTOLOAD),
26 "liveTrafficEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
27 "scansEnabled_checkReadableConfig" => array('value' => true, 'autoload' => self::AUTOLOAD),
28 "advancedCommentScanning" => array('value' => false, 'autoload' => self::AUTOLOAD),
29 "checkSpamIP" => array('value' => false, 'autoload' => self::AUTOLOAD),
30 "spamvertizeCheck" => array('value' => false, 'autoload' => self::AUTOLOAD),
31 "liveTraf_ignorePublishers" => array('value' => true, 'autoload' => self::AUTOLOAD),
32 //"perfLoggingEnabled" => array('value' => false, 'autoload' => self::AUTOLOAD),
33 "scheduledScansEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
34 "scansEnabled_public" => array('value' => false, 'autoload' => self::AUTOLOAD),
35 "scansEnabled_heartbleed" => array('value' => true, 'autoload' => self::AUTOLOAD),
36 "scansEnabled_core" => array('value' => true, 'autoload' => self::AUTOLOAD),
37 "scansEnabled_themes" => array('value' => false, 'autoload' => self::AUTOLOAD),
38 "scansEnabled_plugins" => array('value' => false, 'autoload' => self::AUTOLOAD),
39 "scansEnabled_coreUnknown" => array('value' => true, 'autoload' => self::AUTOLOAD),
40 "scansEnabled_malware" => array('value' => true, 'autoload' => self::AUTOLOAD),
41 "scansEnabled_fileContents" => array('value' => true, 'autoload' => self::AUTOLOAD),
42 "scansEnabled_posts" => array('value' => true, 'autoload' => self::AUTOLOAD),
43 "scansEnabled_comments" => array('value' => true, 'autoload' => self::AUTOLOAD),
44 "scansEnabled_passwds" => array('value' => true, 'autoload' => self::AUTOLOAD),
45 "scansEnabled_diskSpace" => array('value' => true, 'autoload' => self::AUTOLOAD),
46 "scansEnabled_options" => array('value' => true, 'autoload' => self::AUTOLOAD),
47 "scansEnabled_wpscan_fullPathDisclosure" => array('value' => true, 'autoload' => self::AUTOLOAD),
48 "scansEnabled_wpscan_directoryListingEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
49 "scansEnabled_dns" => array('value' => true, 'autoload' => self::AUTOLOAD),
50 "scansEnabled_scanImages" => array('value' => false, 'autoload' => self::AUTOLOAD),
51 "scansEnabled_highSense" => array('value' => false, 'autoload' => self::AUTOLOAD),
52 "scansEnabled_oldVersions" => array('value' => true, 'autoload' => self::AUTOLOAD),
53 "scansEnabled_suspiciousAdminUsers" => array('value' => true, 'autoload' => self::AUTOLOAD),
54 "firewallEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
55 "blockFakeBots" => array('value' => false, 'autoload' => self::AUTOLOAD),
56 "autoBlockScanners" => array('value' => true, 'autoload' => self::AUTOLOAD),
57 "loginSecurityEnabled" => array('value' => true, 'autoload' => self::AUTOLOAD),
58 "loginSec_lockInvalidUsers" => array('value' => false, 'autoload' => self::AUTOLOAD),
59 "loginSec_maskLoginErrors" => array('value' => true, 'autoload' => self::AUTOLOAD),
60 "loginSec_blockAdminReg" => array('value' => true, 'autoload' => self::AUTOLOAD),
61 "loginSec_disableAuthorScan" => array('value' => true, 'autoload' => self::AUTOLOAD),
62 "loginSec_disableOEmbedAuthor" => array('value' => false, 'autoload' => self::AUTOLOAD),
63 "other_hideWPVersion" => array('value' => true, 'autoload' => self::AUTOLOAD),
64 "other_noAnonMemberComments" => array('value' => true, 'autoload' => self::AUTOLOAD),
65 "other_blockBadPOST" => array('value' => false, 'autoload' => self::AUTOLOAD),
66 "other_scanComments" => array('value' => true, 'autoload' => self::AUTOLOAD),
67 "other_pwStrengthOnUpdate" => array('value' => true, 'autoload' => self::AUTOLOAD),
68 "other_WFNet" => array('value' => true, 'autoload' => self::AUTOLOAD),
69 "other_scanOutside" => array('value' => false, 'autoload' => self::AUTOLOAD),
70 "deleteTablesOnDeact" => array('value' => false, 'autoload' => self::AUTOLOAD),
71 "autoUpdate" => array('value' => false, 'autoload' => self::AUTOLOAD),
72 "disableCookies" => array('value' => false, 'autoload' => self::AUTOLOAD),
73 "startScansRemotely" => array('value' => false, 'autoload' => self::AUTOLOAD),
74 "disableConfigCaching" => array('value' => false, 'autoload' => self::AUTOLOAD),
75 "addCacheComment" => array('value' => false, 'autoload' => self::AUTOLOAD),
76 "disableCodeExecutionUploads" => array('value' => false, 'autoload' => self::AUTOLOAD),
77 "allowHTTPSCaching" => array('value' => false, 'autoload' => self::AUTOLOAD),
78 "debugOn" => array('value' => false, 'autoload' => self::AUTOLOAD),
79 'email_summary_enabled' => array('value' => true, 'autoload' => self::AUTOLOAD),
80 'email_summary_dashboard_widget_enabled' => array('value' => true, 'autoload' => self::AUTOLOAD),
81 'ssl_verify' => array('value' => true, 'autoload' => self::AUTOLOAD),
82 'ajaxWatcherDisabled_front' => array('value' => false, 'autoload' => self::AUTOLOAD),
83 'ajaxWatcherDisabled_admin' => array('value' => false, 'autoload' => self::AUTOLOAD),
84 ),
85 "otherParams" => array(
86 "scan_include_extra" => "",
87 // 'securityLevel' => '2',
88 "alertEmails" => "", "liveTraf_ignoreUsers" => "", "liveTraf_ignoreIPs" => "", "liveTraf_ignoreUA" => "", "apiKey" => "", "maxMem" => '256', 'scan_exclude' => '', 'whitelisted' => '', 'bannedURLs' => '', 'maxExecutionTime' => '', 'howGetIPs' => '', 'actUpdateInterval' => '', 'alert_maxHourly' => 0, 'loginSec_userBlacklist' => '',
89 'liveTraf_maxRows' => 2000,
90 "neverBlockBG" => "neverBlockVerified",
91 "loginSec_countFailMins" => "240",
92 "loginSec_lockoutMins" => "240",
93 'loginSec_strongPasswds' => 'pubs',
94 'loginSec_maxFailures' => "20",
95 'loginSec_maxForgotPasswd' => "20",
96 'maxGlobalRequests' => "DISABLED",
97 'maxGlobalRequests_action' => "throttle",
98 'maxRequestsCrawlers' => "DISABLED",
99 'maxRequestsCrawlers_action' => "throttle",
100 'maxRequestsHumans' => "DISABLED",
101 'maxRequestsHumans_action' => "throttle",
102 'max404Crawlers' => "DISABLED",
103 'max404Crawlers_action' => "throttle",
104 'max404Humans' => "DISABLED",
105 'max404Humans_action' => "throttle",
106 'maxScanHits' => "DISABLED",
107 'maxScanHits_action' => "throttle",
108 'blockedTime' => "300",
109 'email_summary_interval' => 'biweekly',
110 'email_summary_excluded_directories' => 'wp-content/cache,wp-content/wfcache,wp-content/plugins/wordfence/tmp',
111 'allowed404s' => "/favicon.ico\n/apple-touch-icon*.png\n/*@2x.png",
112 )
113 );
114 public static $serializedOptions = array('lastAdminLogin', 'scanSched', 'emailedIssuesList', 'wf_summaryItems', 'adminUserList', 'twoFactorUsers', 'alertFreqTrack', 'wfStatusStartMsgs');
115 public static function setDefaults() {
116 foreach (self::$defaultConfig['checkboxes'] as $key => $config) {
117 $val = $config['value'];
118 $autoload = $config['autoload'];
119 if (self::get($key) === false) {
120 self::set($key, $val ? '1' : '0', $autoload);
121 }
122 }
123 foreach (self::$defaultConfig['otherParams'] as $key => $val) {
124 if (self::get($key) === false) {
125 self::set($key, $val);
126 }
127 }
128 self::set('encKey', substr(wfUtils::bigRandomHex(), 0, 16));
129 if (self::get('maxMem', false) === false) {
130 self::set('maxMem', '256');
131 }
132 if (self::get('other_scanOutside', false) === false) {
133 self::set('other_scanOutside', 0);
134 }
135
136 if (self::get('email_summary_enabled')) {
137 wfActivityReport::scheduleCronJob();
138 } else {
139 wfActivityReport::disableCronJob();
140 }
141 }
142 public static function loadAllOptions() {
143 global $wpdb;
144
145 $options = wp_cache_get('alloptions', 'wordfence');
146 if (!$options) {
147 $table = self::table();
148 self::updateTableExists();
149 $suppress = $wpdb->suppress_errors();
150 if (!($rawOptions = $wpdb->get_results("SELECT name, val FROM {$table} WHERE autoload = 'yes'"))) {
151 $rawOptions = $wpdb->get_results("SELECT name, val FROM {$table}");
152 }
153 $wpdb->suppress_errors($suppress);
154 $options = array();
155 foreach ((array) $rawOptions as $o) {
156 if (in_array($o->name, self::$serializedOptions)) {
157 $val = maybe_unserialize($o->val);
158 if ($val) {
159 $options[$o->name] = $val;
160 }
161 }
162 else {
163 $options[$o->name] = $o->val;
164 }
165 }
166
167 wp_cache_add_non_persistent_groups('wordfence');
168 wp_cache_add('alloptions', $options, 'wordfence');
169 }
170
171 return $options;
172 }
173 public static function updateTableExists() {
174 $table = self::table();
175 self::$tableExists = (strtolower(self::getDB()->querySingle("SHOW TABLES LIKE '%s'", $table)) == strtolower($table));
176 }
177 private static function updateCachedOption($name, $val) {
178 $options = self::loadAllOptions();
179 $options[$name] = $val;
180 wp_cache_set('alloptions', $options, 'wordfence');
181 }
182 private static function removeCachedOption($name) {
183 $options = self::loadAllOptions();
184 if (isset($options[$name])) {
185 unset($options[$name]);
186 wp_cache_set('alloptions', $options, 'wordfence');
187 }
188 }
189 private static function getCachedOption($name) {
190 $options = self::loadAllOptions();
191 if (isset($options[$name])) {
192 return $options[$name];
193 }
194
195 $table = self::table();
196 $val = self::getDB()->querySingle("SELECT val FROM {$table} WHERE name='%s'", $name);
197 if ($val !== null) {
198 $options[$name] = $val;
199 wp_cache_set('alloptions', $options, 'wordfence');
200 }
201 return $val;
202 }
203 private static function hasCachedOption($name) {
204 $options = self::loadAllOptions();
205 return isset($options[$name]);
206 }
207 public static function getExportableOptionsKeys(){
208 $ret = array();
209 foreach(self::$defaultConfig['checkboxes'] as $key => $val){
210 $ret[] = $key;
211 }
212 foreach(self::$defaultConfig['otherParams'] as $key => $val){
213 if($key != 'apiKey'){
214 $ret[] = $key;
215 }
216 }
217 foreach(array('cbl_action', 'cbl_countries', 'cbl_redirURL', 'cbl_loggedInBlocked', 'cbl_loginFormBlocked', 'cbl_restOfSiteBlocked', 'cbl_bypassRedirURL', 'cbl_bypassRedirDest', 'cbl_bypassViewURL') as $key){
218 $ret[] = $key;
219 }
220 return $ret;
221 }
222 public static function parseOptions(){
223 $ret = array();
224 foreach(self::$defaultConfig['checkboxes'] as $key => $val){ //value is not used. We just need the keys for validation
225 $ret[$key] = isset($_POST[$key]) ? '1' : '0';
226 }
227 foreach(self::$defaultConfig['otherParams'] as $key => $val){
228 if(isset($_POST[$key])){
229 $ret[$key] = stripslashes($_POST[$key]);
230 } else {
231 error_log("Missing options param \"$key\" when parsing parameters.");
232 }
233 }
234 /* for debugging only:
235 foreach($_POST as $key => $val){
236 if($key != 'action' && $key != 'nonce' && (! array_key_exists($key, self::$checkboxes)) && (! array_key_exists($key, self::$otherParams)) ){
237 error_log("Unrecognized option: $key");
238 }
239 }
240 */
241 return $ret;
242 }
243 public static function setArray($arr){
244 foreach($arr as $key => $val){
245 self::set($key, $val);
246 }
247 }
248 public static function getHTML($key){
249 return esc_html(self::get($key));
250 }
251 public static function inc($key){
252 $val = self::get($key, false);
253 if(! $val){
254 $val = 0;
255 }
256 self::set($key, $val + 1);
257 }
258 public static function set($key, $val, $autoload = self::AUTOLOAD) {
259 global $wpdb;
260
261 if (is_array($val)) {
262 $msg = "wfConfig::set() got an array as second param with key: $key and value: " . var_export($val, true);
263 wordfence::status(1, 'error', $msg);
264 return;
265 }
266
267 if (($key == 'apiKey' || $key == 'isPaid') && wfWAF::getInstance() && !WFWAF_SUBDIRECTORY_INSTALL) {
268 try {
269 wfWAF::getInstance()->getStorageEngine()->setConfig($key, $val);
270 } catch (wfWAFStorageFileException $e) {
271 error_log($e->getMessage());
272 }
273 }
274
275 if (!self::$tableExists) {
276 return;
277 }
278
279 $table = self::table();
280 if ($wpdb->query($wpdb->prepare("INSERT INTO {$table} (name, val, autoload) values (%s, %s, %s) ON DUPLICATE KEY UPDATE val = %s, autoload = %s", $key, $val, $autoload, $val, $autoload)) !== false && $autoload != self::DONT_AUTOLOAD) {
281 self::updateCachedOption($key, $val);
282 }
283 }
284 public static function get($key, $default = false) {
285 global $wpdb;
286
287 if (self::hasCachedOption($key)) {
288 return self::getCachedOption($key);
289 }
290
291 if (!self::$tableExists) {
292 return $default;
293 }
294
295 $table = self::table();
296 if (!($option = $wpdb->get_row($wpdb->prepare("SELECT name, val, autoload FROM {$table} WHERE name = %s", $key)))) {
297 return $default;
298 }
299
300 if ($option->autoload != self::DONT_AUTOLOAD) {
301 self::updateCachedOption($key, $option->val);
302 }
303 return $option->val;
304 }
305
306 private static function canCompressValue() {
307 if (!function_exists('gzencode') || !function_exists('gzdecode')) {
308 return false;
309 }
310 $disabled = explode(',', ini_get('disable_functions'));
311 if (in_array('gzencode', $disabled) || in_array('gzdecode', $disabled)) {
312 return false;
313 }
314 return true;
315 }
316
317 private static function isCompressedValue($data) {
318 //Based on http://www.ietf.org/rfc/rfc1952.txt
319 if (strlen($data) < 2) {
320 return false;
321 }
322
323 $magicBytes = substr($data, 0, 2);
324 if ($magicBytes !== (chr(0x1f) . chr(0x8b))) {
325 return false;
326 }
327
328 //Small chance of false positives here -- can check the header CRC if it turns out it's needed
329 return true;
330 }
331
332 private static function ser_chunked_key($key) {
333 return 'wordfence_chunked_' . $key . '_';
334 }
335
336 public static function get_ser($key, $default, $cache = true) {
337 if (self::hasCachedOption($key)) {
338 return self::getCachedOption($key);
339 }
340
341 if (!self::$tableExists) {
342 return $default;
343 }
344
345 //Check for a chunked value first
346 $chunkedValueKey = self::ser_chunked_key($key);
347 $header = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . 'header');
348 if ($header) {
349 $header = unserialize($header);
350 $count = $header['count'];
351 $path = tempnam(sys_get_temp_dir(), $key); //Writing to a file like this saves some of PHP's in-memory copying when just appending each chunk to a string
352 $fh = fopen($path, 'r+');
353 $length = 0;
354 for ($i = 0; $i < $count; $i++) {
355 $chunk = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . $i);
356 self::getDB()->flush(); //clear cache
357 if (!$chunk) {
358 wordfence::status(2, 'error', "Error reassembling value for {$key}");
359 return $default;
360 }
361 fwrite($fh, $chunk);
362 $length += strlen($chunk);
363 unset($chunk);
364 }
365
366 fseek($fh, 0);
367 $serialized = fread($fh, $length);
368 fclose($fh);
369 unlink($path);
370
371 if (self::canCompressValue() && self::isCompressedValue($serialized)) {
372 $inflated = @gzdecode($serialized);
373 if ($inflated !== false) {
374 unset($serialized);
375 if ($cache) {
376 self::updateCachedOption($key, unserialize($inflated));
377 return self::getCachedOption($key);
378 }
379 return unserialize($inflated);
380 }
381 }
382 if ($cache) {
383 self::updateCachedOption($key, unserialize($serialized));
384 return self::getCachedOption($key);
385 }
386 return unserialize($serialized);
387 }
388 else {
389 $serialized = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $key);
390 self::getDB()->flush(); //clear cache
391 if ($serialized) {
392 if (self::canCompressValue() && self::isCompressedValue($serialized)) {
393 $inflated = @gzdecode($serialized);
394 if ($inflated !== false) {
395 unset($serialized);
396 return unserialize($inflated);
397 }
398 }
399 if ($cache) {
400 self::updateCachedOption($key, unserialize($serialized));
401 return self::getCachedOption($key);
402 }
403 return unserialize($serialized);
404 }
405 }
406
407 return $default;
408 }
409
410 public static function set_ser($key, $val, $allowCompression = false, $autoload = self::AUTOLOAD) {
411 /*
412 * Because of the small default value for `max_allowed_packet` and `max_long_data_size`, we're stuck splitting
413 * large values into multiple chunks. To minimize memory use, the MySQLi driver is used directly when possible.
414 */
415
416 global $wpdb;
417 $dbh = $wpdb->dbh;
418
419 if (!self::$tableExists) {
420 return;
421 }
422
423 self::delete_ser_chunked($key); //Ensure any old values for a chunked value are deleted first
424
425 if (self::canCompressValue() && $allowCompression) {
426 $data = gzencode(serialize($val));
427 }
428 else {
429 $data = serialize($val);
430 }
431
432 if (!$wpdb->use_mysqli) {
433 $data = bin2hex($data);
434 }
435
436 $dataLength = strlen($data);
437 $chunkSize = intval((self::getDB()->getMaxAllowedPacketBytes() - 50) / 1.2); //Based on max_allowed_packet + 20% for escaping and SQL
438 $chunkSize = $chunkSize - ($chunkSize % 2); //Ensure it's even
439 $chunkedValueKey = self::ser_chunked_key($key);
440 if ($dataLength > $chunkSize) {
441 $chunks = 0;
442 while (($chunks * $chunkSize) < $dataLength) {
443 $dataChunk = substr($data, $chunks * $chunkSize, $chunkSize);
444 if ($wpdb->use_mysqli) {
445 $chunkKey = $chunkedValueKey . $chunks;
446 $stmt = $dbh->prepare("INSERT IGNORE INTO " . self::table() . " (name, val, autoload) VALUES (?, ?, 'no')");
447 $null = NULL;
448 $stmt->bind_param("sb", $chunkKey, $null);
449
450 if (!$stmt->send_long_data(1, $dataChunk)) {
451 wordfence::status(2, 'error', "Error writing value chunk for {$key} (MySQLi error: [{$dbh->errno}] {$dbh->error})");
452 return false;
453 }
454
455 if (!$stmt->execute()) {
456 wordfence::status(2, 'error', "Error finishing writing value for {$key} (MySQLi error: [{$dbh->errno}] {$dbh->error})");
457 return false;
458 }
459 }
460 else {
461 if (!self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', 'no')", $dataChunk), $chunkedValueKey . $chunks)) {
462 $errno = mysql_errno($wpdb->dbh);
463 wordfence::status(2, 'error', "Error writing value chunk for {$key} (MySQL error: [$errno] {$wpdb->last_error})");
464 return false;
465 }
466 }
467 $chunks++;
468 }
469
470 if (!self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', 'no')", bin2hex(serialize(array('count' => $chunks)))), $chunkedValueKey . 'header')) {
471 wordfence::status(2, 'error', "Error writing value header for {$key}");
472 return false;
473 }
474 }
475 else {
476 $exists = self::getDB()->querySingle("select name from " . self::table() . " where name='%s'", $key);
477
478 if ($wpdb->use_mysqli) {
479 if ($exists) {
480 $stmt = $dbh->prepare("UPDATE " . self::table() . " SET val=? WHERE name=?");
481 $null = NULL;
482 $stmt->bind_param("bs", $null, $key);
483 }
484 else {
485 $stmt = $dbh->prepare("INSERT IGNORE INTO " . self::table() . " (val, name, autoload) VALUES (?, ?, ?)");
486 $null = NULL;
487 $stmt->bind_param("bss", $null, $key, $autoload);
488 }
489
490 if (!$stmt->send_long_data(0, $data)) {
491 wordfence::status(2, 'error', "Error writing value for {$key} (MySQLi error: [{$dbh->errno}] {$dbh->error})");
492 return false;
493 }
494
495 if (!$stmt->execute()) {
496 wordfence::status(2, 'error', "Error finishing writing value for {$key} (MySQLi error: [{$dbh->errno}] {$dbh->error})");
497 return false;
498 }
499 }
500 else {
501 if ($exists) {
502 self::getDB()->queryWrite(sprintf("update " . self::table() . " set val=X'%s' where name=%%s", $data), $key);
503 }
504 else {
505 self::getDB()->queryWrite(sprintf("insert ignore into " . self::table() . " (name, val, autoload) values (%%s, X'%s', %%s)", $data), $key, $autoload);
506 }
507 }
508 }
509 self::getDB()->flush();
510
511 if ($autoload != self::DONT_AUTOLOAD) {
512 self::updateCachedOption($key, $val);
513 }
514 return true;
515 }
516
517 private static function delete_ser_chunked($key) {
518 if (!self::$tableExists) {
519 return;
520 }
521
522 self::removeCachedOption($key);
523
524 $chunkedValueKey = self::ser_chunked_key($key);
525 $header = self::getDB()->querySingle("select val from " . self::table() . " where name=%s", $chunkedValueKey . 'header');
526 if (!$header) {
527 return;
528 }
529
530 $header = unserialize($header);
531 $count = $header['count'];
532 for ($i = 0; $i < $count; $i++) {
533 self::getDB()->queryWrite("delete from " . self::table() . " where name='%s'", $chunkedValueKey . $i);
534 }
535 self::getDB()->queryWrite("delete from " . self::table() . " where name='%s'", $chunkedValueKey . 'header');
536 }
537 public static function f($key){
538 echo esc_attr(self::get($key));
539 }
540 public static function cbp($key){
541 if(self::get('isPaid') && self::get($key)){
542 echo ' checked ';
543 }
544 }
545 public static function cb($key){
546 if(self::get($key)){
547 echo ' checked ';
548 }
549 }
550 public static function sel($key, $val, $isDefault = false){
551 if((! self::get($key)) && $isDefault){ echo ' selected '; }
552 if(self::get($key) == $val){ echo ' selected '; }
553 }
554 private static function getDB(){
555 if(! self::$DB){
556 self::$DB = new wfDB();
557 }
558 return self::$DB;
559 }
560 private static function table(){
561 if(! self::$table){
562 global $wpdb;
563 self::$table = $wpdb->base_prefix . 'wfConfig';
564 }
565 return self::$table;
566 }
567 public static function haveAlertEmails(){
568 $emails = self::getAlertEmails();
569 return sizeof($emails) > 0 ? true : false;
570 }
571 public static function getAlertEmails(){
572 $dat = explode(',', self::get('alertEmails'));
573 $emails = array();
574 foreach($dat as $email){
575 if(preg_match('/\@/', $email)){
576 $emails[] = trim($email);
577 }
578 }
579 return $emails;
580 }
581 public static function getAlertLevel(){
582 if(self::get('alertOn_warnings')){
583 return 2;
584 } else if(self::get('alertOn_critical')){
585 return 1;
586 } else {
587 return 0;
588 }
589 }
590 public static function liveTrafficEnabled(){
591 if( (! self::get('liveTrafficEnabled')) || self::get('cacheType') == 'falcon' || self::get('cacheType') == 'php'){ return false; }
592 return true;
593 }
594 public static function enableAutoUpdate(){
595 wfConfig::set('autoUpdate', '1');
596 wp_clear_scheduled_hook('wordfence_daily_autoUpdate');
597 if (is_main_site()) {
598 wp_schedule_event(time(), 'daily', 'wordfence_daily_autoUpdate');
599 }
600 }
601 public static function disableAutoUpdate(){
602 wfConfig::set('autoUpdate', '0');
603 wp_clear_scheduled_hook('wordfence_daily_autoUpdate');
604 }
605 public static function autoUpdate(){
606 try {
607 if(getenv('noabort') != '1' && stristr($_SERVER['SERVER_SOFTWARE'], 'litespeed') !== false){
608 $lastEmail = self::get('lastLiteSpdEmail', false);
609 if( (! $lastEmail) || (time() - (int)$lastEmail > (86400 * 30))){
610 self::set('lastLiteSpdEmail', time());
611 wordfence::alert("Wordfence Upgrade not run. Please modify your .htaccess", "To preserve the integrity of your website we are not running Wordfence auto-update.\n" .
612 "You are running the LiteSpeed web server which has been known to cause a problem with Wordfence auto-update.\n" .
613 "Please go to your website now and make a minor change to your .htaccess to fix this.\n" .
614 "You can find out how to make this change at:\n" .
615 "https://docs.wordfence.com/en/LiteSpeed_aborts_Wordfence_scans_and_updates._How_do_I_prevent_that%3F\n" .
616 "\nAlternatively you can disable auto-update on your website to stop receiving this message and upgrade Wordfence manually.\n",
617 '127.0.0.1'
618 );
619 }
620 return;
621 }
622 require_once(ABSPATH . 'wp-admin/includes/class-wp-upgrader.php');
623 require_once(ABSPATH . 'wp-admin/includes/misc.php');
624 /* We were creating show_message here so that WP did not write to STDOUT. This had the strange effect of throwing an error about redeclaring show_message function, but only when a crawler hit the site and triggered the cron job. Not a human. So we're now just require'ing misc.php which does generate output, but that's OK because it is a loopback cron request.
625 if(! function_exists('show_message')){
626 function show_message($msg = 'null'){}
627 }
628 */
629 if(! defined('FS_METHOD')){
630 define('FS_METHOD', 'direct'); //May be defined already and might not be 'direct' so this could cause problems. But we were getting reports of a warning that this is already defined, so this check added.
631 }
632 require_once(ABSPATH . 'wp-includes/update.php');
633 require_once(ABSPATH . 'wp-admin/includes/file.php');
634 wp_update_plugins();
635 ob_start();
636 $upgrader = new Plugin_Upgrader();
637 $upret = $upgrader->upgrade(WORDFENCE_BASENAME);
638 if($upret){
639 $cont = file_get_contents(WORDFENCE_FCPATH);
640 if(wfConfig::get('alertOn_update') == '1' && preg_match('/Version: (\d+\.\d+\.\d+)/', $cont, $matches) ){
641 wordfence::alert("Wordfence Upgraded to version " . $matches[1], "Your Wordfence installation has been upgraded to version " . $matches[1], '127.0.0.1');
642 }
643 }
644 $output = @ob_get_contents();
645 @ob_end_clean();
646 } catch(Exception $e){}
647 }
648
649 /**
650 * .htaccess file contents to disable all script execution in a given directory.
651 */
652 private static $_disable_scripts_htaccess = '# BEGIN Wordfence code execution protection
653 <IfModule mod_php5.c>
654 php_flag engine 0
655 </IfModule>
656
657 AddHandler cgi-script .php .phtml .php3 .pl .py .jsp .asp .htm .shtml .sh .cgi
658 Options -ExecCGI
659 # END Wordfence code execution protection
660 ';
661
662 private static function _uploadsHtaccessFilePath() {
663 $upload_dir = wp_upload_dir();
664 return $upload_dir['basedir'] . '/.htaccess';
665 }
666
667 /**
668 * Add/Merge .htaccess file in the uploads directory to prevent code execution.
669 *
670 * @return bool
671 * @throws wfConfigException
672 */
673 public static function disableCodeExecutionForUploads() {
674 $uploads_htaccess_file_path = self::_uploadsHtaccessFilePath();
675 $uploads_htaccess_has_content = false;
676 if (file_exists($uploads_htaccess_file_path)) {
677 $htaccess_contents = file_get_contents($uploads_htaccess_file_path);
678
679 // htaccess exists and contains our htaccess code to disable script execution, nothing more to do
680 if (strpos($htaccess_contents, self::$_disable_scripts_htaccess) !== false) {
681 return true;
682 }
683 $uploads_htaccess_has_content = strlen(trim($htaccess_contents)) > 0;
684 }
685 if (@file_put_contents($uploads_htaccess_file_path, ($uploads_htaccess_has_content ? "\n\n" : "") . self::$_disable_scripts_htaccess, FILE_APPEND | LOCK_EX) === false) {
686 throw new wfConfigException("Unable to save the .htaccess file needed to disable script execution in the uploads directory. Please check your permissions on that directory.");
687 }
688 return true;
689 }
690
691 /**
692 * Remove script execution protections for our the .htaccess file in the uploads directory.
693 *
694 * @return bool
695 * @throws wfConfigException
696 */
697 public static function removeCodeExecutionProtectionForUploads() {
698 $uploads_htaccess_file_path = self::_uploadsHtaccessFilePath();
699 if (file_exists($uploads_htaccess_file_path)) {
700 $htaccess_contents = file_get_contents($uploads_htaccess_file_path);
701
702 // Check that it is in the file
703 if (strpos($htaccess_contents, self::$_disable_scripts_htaccess) !== false) {
704 $htaccess_contents = str_replace(self::$_disable_scripts_htaccess, '', $htaccess_contents);
705
706 $error_message = "Unable to remove code execution protections applied to the .htaccess file in the uploads directory. Please check your permissions on that file.";
707 if (strlen(trim($htaccess_contents)) === 0) {
708 // empty file, remove it
709 if (!@unlink($uploads_htaccess_file_path)) {
710 throw new wfConfigException($error_message);
711 }
712
713 } elseif (@file_put_contents($uploads_htaccess_file_path, $htaccess_contents, LOCK_EX) === false) {
714 throw new wfConfigException($error_message);
715 }
716 }
717 }
718 return true;
719 }
720 }
721
722 class wfConfigException extends Exception {}
723
724 ?>
725