PluginProbe ʕ •ᴥ•ʔ
NinjaFirewall (WP Edition) – Advanced Security Plugin and Firewall / trunk
NinjaFirewall (WP Edition) – Advanced Security Plugin and Firewall vtrunk
4.8.8 4.8.7 4.8.6 trunk 4.5 4.5.1 4.5.10 4.5.11 4.5.2 4.5.3 4.5.4 4.5.5 4.5.6 4.5.7 4.5.8 4.5.9 4.6 4.6.1 4.7 4.7.1 4.7.2 4.7.3 4.7.4 4.7.5 4.8 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5
ninjafirewall / lib / helpers.php
ninjafirewall / lib Last commit date
share 8 years ago .htaccess 11 years ago anti_malware.php 4 years ago class-coupon.php 6 months ago class-email-sodium.php 3 weeks ago class-firewall-log.php 1 week ago class-helpers.php 7 months ago class-import-export.php 3 months ago class-ip.php 4 months ago class-nfw-database.php 6 months ago class-nfw-session.php 1 week ago class-php-session.php 1 year ago class-plugin-upgrade.php 1 week ago class_mail.php 3 weeks ago event_updates.php 9 months ago firewall.php 4 months ago fw_centlog.php 1 week ago fw_fileguard.php 4 months ago fw_livelog.php 1 year ago help.php 3 weeks ago helpers.php 1 week ago i18n-extra.php 3 weeks ago i18n.php 1 year ago index.html 13 years ago init_update.php 1 year ago install.php 1 year ago install_default.php 6 months ago loader.php 6 months ago mail_template_firewall.php 1 year ago mail_template_plugin.php 1 month ago scheduled_tasks.php 3 years ago settings_dashboard.php 1 month ago settings_dashboard_about.php 3 weeks ago settings_dashboard_statistics.php 1 month ago settings_event_notifications.php 1 month ago settings_events.php 1 month ago settings_firewall_options.php 1 month ago settings_firewall_policies.php 1 week ago settings_login_protection.php 1 month ago settings_logs.php 1 month ago settings_logs_firewall_log.php 3 weeks ago settings_logs_live_log.php 1 month ago settings_monitoring.php 3 weeks ago settings_monitoring_file_check.php 1 month ago settings_monitoring_file_guard.php 1 month ago settings_network.php 1 month ago settings_security_rules.php 1 month ago settings_security_rules_editor.php 1 month ago settings_security_rules_update.php 1 week ago sign.pub 7 years ago thickbox.php 4 years ago widget.php 3 years ago wpplus.php 3 months ago
helpers.php
1812 lines
1 <?php
2 /*
3 +=====================================================================+
4 | _ _ _ _ _____ _ _ _ |
5 | | \ | (_)_ __ (_) __ _| ___(_)_ __ _____ ____ _| | | |
6 | | \| | | '_ \ | |/ _` | |_ | | '__/ _ \ \ /\ / / _` | | | |
7 | | |\ | | | | || | (_| | _| | | | | __/\ V V / (_| | | | |
8 | |_| \_|_|_| |_|/ |\__,_|_| |_|_| \___| \_/\_/ \__,_|_|_| |
9 | |__/ |
10 | (c) NinTechNet Limited ~ https://nintechnet.com/ |
11 +=====================================================================+
12 */
13
14 if (! defined('NFW_ENGINE_VERSION') ) { die('Forbidden'); }
15
16 // --------------------------------------------------------------------- 2023-07-27
17 // The name of the MU plugin can be defined in wp-config.php.
18
19 if (! defined('NINJAFIREWALL_MU_PLUGIN') ) {
20 define('NINJAFIREWALL_MU_PLUGIN', '0-ninjafirewall.php');
21 } else {
22 // If defined and different, make sure to delete the old one
23 if ( NINJAFIREWALL_MU_PLUGIN != '0-ninjafirewall.php' &&
24 file_exists( WPMU_PLUGIN_DIR .'/0-ninjafirewall.php') ) {
25
26 unlink( WPMU_PLUGIN_DIR .'/0-ninjafirewall.php');
27 }
28 }
29
30 // ---------------------------------------------------------------------
31 // Contextual help reminder.
32
33 function nfw_contextual_help() {
34 echo '<div style="text-align:right;font-weight:normal;padding-top: 9px;">' .
35 '<span class="description" style="color:#808080;">' .
36 esc_html('Click on the above "Help" tab for help.', 'ninjafirewall') .
37 '</span></div>';
38 }
39
40 // --------------------------------------------------------------------- 2023-07-27
41 // Animated button/switch.
42
43 function nfw_toggle_switch( $type, $name, $text_on, $text_off,
44 $size, $value = 0, $disabled = false, $attr = false,
45 $id = false, $align = false ) {
46
47 if ( $size == 'large') {
48 $size = 'style="width:150px;"';
49
50 } elseif ( $size == 'small') {
51 $size = 'style="width:80px"';
52
53 } else {
54 $size = 'style="width:'. (int) $size .'px"';
55 }
56
57 if ( $type == 'danger') {
58 $type = 'tgl-danger';
59 } elseif ( $type == 'warning') {
60 $type = 'tgl-warning';
61 } elseif ( $type == 'green') {
62 $type = 'tgl-green';
63 } else {
64 $type = 'tgl-info';
65 }
66
67 $text_on = esc_attr( $text_on );
68 $text_off = esc_attr( $text_off );
69
70 if ( $id == false ) {
71 $id = uniqid();
72 }
73
74 $name = esc_attr( $name );
75 $id = esc_attr( $id );
76
77 if ( $disabled == false ) {
78 $disabled = '';
79 } else {
80 $disabled = ' disabled';
81 }
82 if ( $attr != false ) {
83 $attr = ' '. $attr;
84 }
85
86 if ( $align == false ) {
87 $align = '';
88 } elseif ( $align == 'left') {
89 $align = ' alignleft';
90 } else {
91 $align = ' alignright';
92 }
93 ?>
94 <div class="tg-list-item<?php echo $align ?>">
95 <input class="tgl tgl-switch" name="<?php echo $name ?>"<?php checked( $value, 1 ) ?> id="<?php echo $id ?>" type="checkbox"<?php echo $disabled; ?><?php echo $attr ?> />
96 <label class="tgl-btn <?php echo $type ?>"<?php nfw_aria_label( $value, 1, $text_on, $text_off ) ?> data-tg-on="<?php echo $text_on ?>" data-tg-off="<?php echo $text_off ?>" for="<?php echo $id ?>" <?php echo $size ?>></label>
97 </div>
98 <?php
99 }
100
101 // ---------------------------------------------------------------------
102 // Check for HTTPS. This function is also available in firewall.php
103 // and is used here only if the firewall is not loaded.
104
105 if (! function_exists('nfw_is_https') ) {
106
107 function nfw_is_https() {
108 // Can be defined in the .htninja:
109 if ( defined('NFW_IS_HTTPS') ) { return; }
110
111 if ( ( isset( $_SERVER['SERVER_PORT'] ) && $_SERVER['SERVER_PORT'] == 443 ) ||
112 ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') ||
113 ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off') ) {
114 define('NFW_IS_HTTPS', true);
115 } else {
116 define('NFW_IS_HTTPS', false);
117 }
118 }
119 }
120 nfw_is_https();
121
122 // ---------------------------------------------------------------------
123 // Check whether the user is whitelisted (.htninja etc).
124
125 function nfw_is_whitelisted() {
126
127 if ( defined('NFW_UWL') && NFW_UWL == true ) {
128 return true;
129 }
130 }
131
132 // ---------------------------------------------------------------------
133
134 add_filter('wp_insert_post_empty_content', 'nf_wp_insert_post_empty_content', 10000, 2 );
135
136 function nf_wp_insert_post_empty_content( $maybe_empty, $postarr ) {
137
138 $nfw_options = nfw_get_option('nfw_options');
139
140 if (! empty( NinjaFirewall_session::read('nfw_goodguy') ) || nfw_is_whitelisted() ||
141 empty( $nfw_options['enabled'] ) || empty( $nfw_options['disallow_publish'] ) ) {
142
143 return false;
144 }
145
146 /**
147 * We only care about page and post post_type.
148 */
149 if (! empty( $postarr['post_type'] ) &&
150 ( $postarr['post_type'] == 'post' || $postarr['post_type'] == 'page') ) {
151
152 if (! isset( $postarr['ID'] ) ) {
153 $id = 0;
154 } else {
155 $id = $postarr['ID'];
156 }
157
158 /**
159 * Ignore post if it isn't either already published or set to be published immediately.
160 */
161 if ( get_post_status( $id ) != 'publish' &&
162 ( empty( $postarr['post_status'] ) || $postarr['post_status'] != 'publish') ) {
163
164 return false;
165 }
166
167 /**
168 * Ignore empty post whose ID is 0, including issue with the Quick Draft widget (#2140).
169 */
170 if ( empty( $id ) && empty( $postarr['post_content'] ) ) {
171 return false;
172 }
173
174 $old_post = get_post( $id );
175 if ( $old_post->post_title == $postarr['post_title'] &&
176 $old_post->post_content == $postarr['post_content'] ) {
177
178 return false;
179 }
180
181 /**
182 * We must use meta capability (edit_post/edit_page), not capability (edit_postS/edit_pageS).
183 */
184 $edit_post = "edit_{$postarr['post_type']}";
185 if ( current_user_can( $edit_post, $id ) ) {
186 return false;
187 }
188
189 if (! empty( $postarr['post_title'] ) ) {
190 $post_title = $postarr['post_title'];
191 } else {
192 $post_title = __('N/A', 'ninjafirewall');
193 }
194 if (! empty( $postarr['post_content'] ) ) {
195 if ( strlen( $postarr['post_content'] ) > 100 ) {
196 $postarr['post_content'] = mb_substr( $postarr['post_content'], 0, 100, 'utf-8') .'...';
197 }
198 $post_content = $postarr['post_content'];
199 } else {
200 $post_content = __('N/A', 'ninjafirewall');
201 }
202
203 /**
204 * Page or post creation.
205 */
206 if ( empty( $id ) ) {
207 /* Translators : "page" or "post" type */
208 $action = sprintf( __('Attempt to create a new %s', 'ninjafirewall'), $postarr['post_type'] );
209 /**
210 * Page or post edition.
211 */
212 } else {
213 /* Translators : "page" or "post" type and its numerical ID */
214 $action = sprintf(
215 __('Attempt to edit a published %s (ID: %s)', 'ninjafirewall'), $postarr['post_type'], $id
216 );
217 }
218
219 /**
220 * Check if the user is authenticated.
221 */
222 $current_user = wp_get_current_user();
223 if ( empty( $current_user->user_login ) ) {
224 $user = __('Unauthenticated user', 'ninjafirewall');
225 } else {
226 $user = $current_user->user_login;
227 }
228
229 $subject = __('Blocked post/page edition attempt', 'ninjafirewall');
230
231 NinjaFirewall_log::write(
232 "WordPress: $subject",
233 "post_content: $post_content",
234 NFWLOG_CRITICAL, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
235 );
236
237 /**
238 * Backtrace.
239 */
240 $return = nfw_debug_backtrace( $nfw_options );
241 if (! empty( $return['nftmpfname'] ) ) {
242 $attachment = $return['nftmpfname'];
243 } else {
244 $attachment = [];
245 }
246
247 /**
248 * Email notification.
249 */
250 $subject = [];
251 $content = [ home_url('/'), $user, $action, $post_title, $post_content,
252 NFW_REMOTE_ADDR, $_SERVER['SCRIPT_FILENAME'], $_SERVER['REQUEST_URI'],
253 date_i18n('F j, Y @ H:i:s T'), $return['message']
254 ];
255 NinjaFirewall_mail::send('perm_edit', $subject, $content, '', $attachment, 1 );
256
257
258 /**
259 * Block the request.
260 */
261 NinjaFirewall_session::delete();
262 wp_die(
263 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
264 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
265 $nfw_options['ret_code']
266 );
267 }
268 return false;
269 }
270
271 // ---------------------------------------------------------------------
272
273 add_filter('pre_delete_post', 'nf_pre_delete_post', 10000, 3 );
274
275 function nf_pre_delete_post( $delete, $post, $force_delete ) {
276
277 $nfw_options = nfw_get_option('nfw_options');
278
279 if (! empty( NinjaFirewall_session::read('nfw_goodguy') ) || nfw_is_whitelisted() ||
280 empty( $nfw_options['enabled'] ) || empty( $nfw_options['disallow_publish'] ) ) {
281
282 return null;
283 }
284 if (! isset( $post->post_type ) || ! isset( $post->post_status ) || empty( $post->ID ) ) {
285 return null;
286 }
287 if ( ( $post->post_type == 'post' ||
288 $post->post_type == 'page') && $post->post_status == 'publish') {
289
290 if (! current_user_can( "delete_{$post->post_type}", $post->ID ) ) {
291
292 /**
293 * Check if user is authenticated.
294 */
295 $current_user = wp_get_current_user();
296 if ( empty( $current_user->user_login ) ) {
297 $user = __('Unauthenticated user', 'ninjafirewall');
298 } else {
299 $user = $current_user->user_login;
300 }
301
302 if (! empty( $post->post_title ) ) {
303 $post_title = $post->post_title;
304 } else {
305 $post_title = __('N/A', 'ninjafirewall');
306 }
307
308 $subject = __('Blocked post/page deletion attempt', 'ninjafirewall');
309
310 NinjaFirewall_log::write(
311 "WordPress: $subject",
312 "post ID: {$post->ID}",
313 NFWLOG_CRITICAL, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
314 );
315
316 /**
317 * Backtrace.
318 */
319 $return = nfw_debug_backtrace( $nfw_options );
320 if (! empty( $return['nftmpfname'] ) ) {
321 $attachment = $return['nftmpfname'];
322 } else {
323 $attachment = [];
324 }
325
326 /**
327 * Email notification.
328 */
329 $subject = [];
330 $content = [ home_url('/'), $user, $post->ID, $post_title,
331 NFW_REMOTE_ADDR, $_SERVER['SCRIPT_FILENAME'], $_SERVER['REQUEST_URI'],
332 date_i18n('F j, Y @ H:i:s T'), $return['message']
333 ];
334 NinjaFirewall_mail::send('perm_delete', $subject, $content, '', $attachment, 1 );
335
336 /**
337 * Block the request.
338 */
339 NinjaFirewall_session::delete();
340 wp_die(
341 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
342 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
343 $nfw_options['ret_code']
344 );
345 }
346 }
347 return null;
348 }
349
350 // ---------------------------------------------------------------------
351 // Return backtrace verbosity.
352
353 function nfw_verbosity( $nfw_options ) {
354
355 if (! isset( $nfw_options['a_61'] ) || $nfw_options['a_61'] == 1 ) {
356 // Medium verbosity:
357 return 0;
358
359 } elseif ( $nfw_options['a_61'] == -1 ) {
360 // Disabled:
361 return false;
362
363 } elseif ( $nfw_options['a_61'] == 2 ) {
364 // High verbosity:
365 return 1;
366 }
367
368 // Low verbosity:
369 return 2;
370 }
371
372 // ---------------------------------------------------------------------
373 // Prevent account deletion.
374
375 function nfw_delete_user( $user_id ) {
376
377 $nfw_options = nfw_get_option('nfw_options');
378 $user_data = get_userdata( $user_id );
379
380 if ( current_user_can('delete_users') || empty( $nfw_options['disallow_deletion'] ) ||
381 empty( $nfw_options['enabled'] ) ) {
382
383 /**
384 * Log and allow the request.
385 */
386 NinjaFirewall_log::write(
387 "Deleting user",
388 "User: {$user_data->user_login}, ID: $user_id",
389 NFWLOG_INFO, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
390 );
391 return;
392 }
393
394 $subject = __('Blocked user deletion attempt', 'ninjafirewall');
395 /**
396 * Write to log.
397 */
398 NinjaFirewall_log::write(
399 "WordPress: $subject",
400 "User: {$user_data->user_login}, ID: $user_id",
401 NFWLOG_CRITICAL, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
402 );
403
404 /**
405 * Backtrace.
406 */
407 $return = nfw_debug_backtrace( $nfw_options );
408 if (! empty( $return['nftmpfname'] ) ) {
409 $attachment = $return['nftmpfname'];
410 } else {
411 $attachment = [];
412 }
413
414 /**
415 * Email notification.
416 */
417 $subject = [];
418 $content = [ home_url('/'), "{$user_data->user_login} (ID: $user_id)",
419 NFW_REMOTE_ADDR, $_SERVER['SCRIPT_FILENAME'], $_SERVER['REQUEST_URI'],
420 date_i18n('F j, Y @ H:i:s T') , $return['message']
421 ];
422 NinjaFirewall_mail::send('delete_user', $subject, $content, '', $attachment, 1 );
423
424 /**
425 * Block the request.
426 */
427 NinjaFirewall_session::delete();
428 wp_die(
429 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
430 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
431 $nfw_options['ret_code']
432 );
433 }
434
435 add_action('delete_user', 'nfw_delete_user');
436
437 // ---------------------------------------------------------------------
438 // Allow/disallow account creation.
439
440 function nfw_account_creation( $user_login ) {
441
442 $nfw_options = nfw_get_option('nfw_options');
443
444 /**
445 * We must allow the request if the username exists too, otherwise we'll
446 * block them from using the "Lost password" feature.
447 */
448 if ( current_user_can('create_users') || empty( $nfw_options['disallow_creation'] ) ||
449 empty( $nfw_options['enabled'] ) || username_exists( $user_login ) ) {
450 /**
451 * Do nothing.
452 */
453 return $user_login;
454 }
455
456 $subject = __('Blocked user account creation', 'ninjafirewall');
457 /**
458 * Write to log.
459 */
460 NinjaFirewall_log::write(
461 "WordPress: {$subject}",
462 "Username: {$user_login}",
463 NFWLOG_CRITICAL, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
464 );
465
466 /**
467 * Backtrace.
468 */
469 $return = nfw_debug_backtrace( $nfw_options );
470 if (! empty( $return['nftmpfname'] ) ) {
471 $attachment = $return['nftmpfname'];
472 } else {
473 $attachment = [];
474 }
475
476 /**
477 * Email notification.
478 */
479 $subject = [];
480 $content = [ home_url('/'), $user_login, NFW_REMOTE_ADDR, $_SERVER['SCRIPT_FILENAME'],
481 $_SERVER['REQUEST_URI'], date_i18n('F j, Y @ H:i:s T') , $return['message']
482 ];
483 NinjaFirewall_mail::send('create_user', $subject, $content, '', $attachment, 1 );
484
485 /**
486 * Block the request.
487 */
488 NinjaFirewall_session::delete();
489 wp_die(
490 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
491 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
492 $nfw_options['ret_code']
493 );
494 }
495
496 add_filter('pre_user_login' , 'nfw_account_creation');
497
498 // ---------------------------------------------------------------------
499 // Clean/delete cache folder & temp files (hourly cron job).
500
501 function nfw_garbage_collector() {
502
503 $path = NFW_LOG_DIR .'/nfwlog/cache';
504 $now = time();
505 // Make sure the cache folder exists, i.e, we have been
506 // through the whole installation process
507 if (! is_dir( $path ) ) {
508 return;
509 }
510
511 // Don't do anything if the garbage collector was executed less than 45mn ago
512 $gc = $path .'/garbage_collector.php';
513 if ( file_exists( $gc ) ) {
514 $nfw_mtime = filemtime( $gc ) ;
515 if ( $now - $nfw_mtime < 45*60 ) {
516 return;
517 }
518 unlink( $gc );
519 }
520 touch( $gc );
521
522 // Fetch options
523 $nfw_options = nfw_get_option('nfw_options');
524
525 // ------------------------------------------------------------------
526 // If nfw_options is corrupted (e.g., failed update etc) we try to restore it
527 // from a backup file otherwise we restore it from the default settings.
528 if ( nfw_validate_option( $nfw_options ) === false ) {
529
530 $files = NinjaFirewall_helpers::nfw_glob( $path, 'backup_.+?\.php$', true, true );
531 $valid_option = 0;
532
533 // Make sure we have a backup file
534 while ( is_array( $files ) && ! empty( $files[0] ) ) {
535 $content = [];
536 $last_file = array_pop( $files );
537 $data = file_get_contents( $last_file );
538 $data = str_replace('<?php exit; ?>', '', $data );
539 // Is it base64-encoded (since 4.3.5)?
540 if ( $data[0] == 'B') {
541 // Decode it
542 $data = ltrim( $data, 'B');
543 $data = base64_decode( $data );
544 }
545 $content = @explode("\n:-:\n", $data . "\n:-:\n");
546 $content[0] = json_decode( $content[0], true );
547
548 if ( nfw_validate_option( $content[0] ) === true ) {
549 // We can use that backup to restore our options
550 $valid_option = 1;
551 break;
552
553 // Delete this corrupted backup file
554 } else {
555 nfw_log_error(
556 sprintf(__('Backup file is corrupted, deleting it (%s)','ninjafirewall'), $last_file )
557 );
558 unlink( $last_file );
559 }
560 }
561
562 // Restore the last good backup
563 if (! empty( $valid_option ) ) {
564 nfw_update_option('nfw_options', $content[0] );
565 nfw_log_error( sprintf( __('NinjaFirewall\'s options are corrupted, restoring them from '.
566 'last known good backup file (%s)', 'ninjafirewall'), $last_file ) );
567
568 // Restore the default settings if no backup file was found
569 // (this action will also restore the firewall rules)
570 } else {
571 require_once __DIR__ .'/install_default.php';
572 nfw_log_error( __('NinjaFirewall\'s options are corrupted, restoring their default values '.
573 '(no valid backup found)', 'ninjafirewall') );
574 nfw_load_default_conf();
575 }
576
577 $nfw_options = nfw_get_option('nfw_options');
578 }
579
580 // ------------------------------------------------------------------
581
582 // Check if we must delete old firewall logs
583 if (! empty( $nfw_options['auto_del_log'] ) ) {
584 $auto_del_log = (int) $nfw_options['auto_del_log'] * 86400;
585
586 // Retrieve the list of all logs
587 $list = NinjaFirewall_helpers::nfw_glob(
588 NFW_LOG_DIR .'/nfwlog', 'firewall_.+?\.php$', true, true
589 );
590
591 foreach( $list as $file ) {
592 $lines = [];
593 $lines = file( $file, FILE_SKIP_EMPTY_LINES );
594 foreach( $lines as $k => $line ) {
595 if ( preg_match('/^\[(\d{10})\]/', $line, $match ) ) {
596 if ( $now - $auto_del_log > $match[1] ) {
597 // This line is too old, remove it
598 unset( $lines[ $k ] );
599 }
600 } else {
601 // Not a proper firewall log line
602 unset( $lines[ $k ] );
603 }
604 }
605 if ( empty( $lines ) ) {
606 // No lines left, delete the file
607 unlink( $file );
608 } else {
609 // Save the last preserved lines to the log
610 $fh = fopen( $file, 'w');
611 fwrite( $fh, "<?php exit; ?>\n" );
612 foreach( $lines as $line ) {
613 fwrite( $fh, $line );
614 }
615 fclose( $fh );
616 }
617 }
618 }
619
620 // File Guard temp files
621 $list = NinjaFirewall_helpers::nfw_glob( $path, 'fg_.+?\.php$', true, true );
622 foreach( $list as $file ) {
623 $nfw_ctime = filectime( $file );
624 // Delete it, if it is too old
625 if ( $now - $nfw_options['fg_mtime'] * 3660 > $nfw_ctime ) {
626 unlink( $file );
627 }
628 }
629
630 /**
631 * Remove older session files if they were untouched for 24mn (1440 sec).
632 * Note: NFWSESS_MAXLIFETIME can be defined in the wp-config.php or .htninja.
633 */
634 if ( defined('NFWSESSION_DIR') ) {
635 if (! defined('NFWSESS_MAXLIFETIME') ) {
636 define('NFWSESS_MAXLIFETIME', 1440);
637 }
638 $list = NinjaFirewall_helpers::nfw_glob( NFWSESSION_DIR, '^sess_', true, true );
639 foreach( $list as $file ) {
640 $sess_time = filemtime( $file );
641 if ( $sess_time + NFWSESS_MAXLIFETIME < $now ) {
642 wp_delete_file( $file );
643 }
644 }
645 }
646
647 // Live Log
648 $nfw_livelogrun = $path . '/livelogrun.php';
649 if ( file_exists( $nfw_livelogrun ) ) {
650 $nfw_mtime = filemtime( $nfw_livelogrun );
651 // If the file was not accessed for more than 100s, we assume
652 // the admin has stopped using live log from WordPress
653 // dashboard (refresh rate is max 45 seconds)
654 if ( $now - $nfw_mtime > 100 ) {
655 unlink( $nfw_livelogrun );
656 }
657 }
658 // If the log was not modified for the past 10mn, we delete it as well
659 $nfw_livelog = $path . '/livelog.php';
660 if ( file_exists( $nfw_livelog ) ) {
661 $nfw_mtime = filemtime( $nfw_livelog ) ;
662 if ( $now - $nfw_mtime > 600 ) {
663 unlink( $nfw_livelog );
664 }
665 }
666
667 // ------------------------------------------------------------------
668
669 // NinjaFirewall's configuration backup. We create a new one daily
670 $list = NinjaFirewall_helpers::nfw_glob( $path, 'backup_.+?\.php$', true, true );
671 if (! empty( $list[0] ) ) {
672 rsort( $list );
673 // Check if last backup if older than one day
674 if ( preg_match('`/backup_(\d{10})_.+\.php$`', $list[0], $match ) ) {
675 if ( $now - $match[1] > 86400 ) {
676 // Backup the configuration
677 $nfw_rules = nfw_get_option('nfw_rules');
678 if ( file_exists( $path .'/bf_conf.php') ) {
679 $bd_data = json_encode( file_get_contents( $path .'/bf_conf.php') );
680 } else {
681 $bd_data = '';
682 }
683 $data = json_encode( $nfw_options ) ."\n:-:\n". json_encode($nfw_rules) ."\n:-:\n". $bd_data;
684 $file = uniqid('backup_'. time() .'_', true) . '.php';
685 // Since version 4.3.5, we base64-encode the data because
686 // some hosts flag it as malicious
687 @file_put_contents( "$path/$file", '<?php exit; ?>B' . base64_encode( $data ), LOCK_EX );
688 array_unshift( $list, "$path/$file" );
689 }
690 }
691 // Keep the last 5 backup only (value can be defined
692 // in the wp-config.php)
693 if ( defined('NFW_MAX_BACKUP') ) {
694 $num = (int) NFW_MAX_BACKUP;
695 } else {
696 $num = 5;
697 }
698 $old_backup = array_slice( $list, $num );
699 foreach( $old_backup as $file ) {
700 unlink( $file );
701 }
702 } else {
703 // Create first backup
704 $nfw_rules = nfw_get_option('nfw_rules');
705 if ( empty( $nfw_rules ) ) {
706 return;
707 }
708 if ( file_exists( $path .'/bf_conf.php') ) {
709 $bd_data = json_encode( file_get_contents( $path .'/bf_conf.php') );
710 } else {
711 $bd_data = '';
712 }
713 $data = json_encode( $nfw_options ) ."\n:-:\n". json_encode( $nfw_rules ) ."\n:-:\n". $bd_data;
714 $file = uniqid('backup_'. time() .'_', true ) .'.php';
715 // Since version 4.3.5, we base64-encode the data because
716 // some hosts flag it as malicious
717 @file_put_contents( "$path/$file", '<?php exit; ?>B' . base64_encode( $data ), LOCK_EX );
718 }
719
720 // ------------------------------------------------------------------
721 // Security updates
722 $nfw_fetchsecupdates = get_transient('nfw_fetchsecupdates');
723 if ( $nfw_fetchsecupdates === false ) {
724 require __DIR__ .'/event_updates.php';
725 nfw_check_security_updates();
726 }
727
728 /**
729 * Check if we have a discount coupon to offer to the user.
730 */
731 require_once __DIR__ .'/class-coupon.php';
732 $coupon = new NinjaFirewall_coupon();
733 $coupon->run();
734 }
735
736 // ---------------------------------------------------------------------
737 // Write potential errors to a specific log.
738
739 function nfw_log_error( $message ) {
740
741 $log = NFW_LOG_DIR . '/nfwlog/error_log.php';
742
743 if (! file_exists( $log ) ) {
744 @file_put_contents( $log, "<?php exit; ?>\n", LOCK_EX );
745 }
746 @file_put_contents( $log, date('[d/M/y:H:i:s O]') . " $message\n", FILE_APPEND | LOCK_EX );
747
748 }
749
750 // ---------------------------------------------------------------------
751
752 function nfw_admin_notice() {
753
754 // Warn about Site Health if needed
755 if ( strpos( $_SERVER['SCRIPT_NAME'], '/wp-admin/site-health.php') !== FALSE ) {
756 // This bug was fixed in WordPress 5.6.1
757 global $wp_version;
758 if ( version_compare( $wp_version, '5.6.1', '<') ) {
759 if ( file_exists( NFW_LOG_DIR . '/nfwlog/cache/bf_conf.php') ) {
760 include NFW_LOG_DIR . '/nfwlog/cache/bf_conf.php';
761 if (! empty( $bf_enable ) ) {
762 echo '<div class="notice-warning notice is-dismissible"><p>'. __('Warning: Because NinjaFirewall\'s Login Protection is enabled, Site Health may return an error message regarding the loopback test (e.g., 404 or 401 HTTP status code). You can safely ignore it.', 'ninjafirewall') .'</p></div>';
763 }
764 }
765 }
766 }
767
768 if (nf_not_allowed( 0, __LINE__ ) ) { return; }
769
770 if (! defined('NF_DISABLED') ) {
771 is_nfw_enabled();
772 }
773
774 if (! file_exists(NFW_LOG_DIR . '/nfwlog') ) {
775 @mkdir( NFW_LOG_DIR . '/nfwlog', 0755);
776 @touch( NFW_LOG_DIR . '/nfwlog/index.html');
777 @file_put_contents(NFW_LOG_DIR . '/nfwlog/.htaccess', "Order Deny,Allow\nDeny from all", LOCK_EX);
778 if (! file_exists(NFW_LOG_DIR . '/nfwlog/cache') ) {
779 @mkdir( NFW_LOG_DIR . '/nfwlog/cache', 0755);
780 @touch( NFW_LOG_DIR . '/nfwlog/cache/index.html');
781 @file_put_contents(NFW_LOG_DIR . '/nfwlog/cache/.htaccess', "Order Deny,Allow\nDeny from all", LOCK_EX);
782 }
783 }
784 if (! file_exists(NFW_LOG_DIR . '/nfwlog') ) {
785 echo '<div class="error notice is-dismissible"><p><strong>' . __('NinjaFirewall error', 'ninjafirewall') . ' :</strong> ' .
786 sprintf( __('%s directory cannot be created. Please review your installation and ensure that %s is writable.', 'ninjafirewall'), '<code>'. esc_html(NFW_LOG_DIR) .'/nfwlog/</code>', '<code>/wp-content/</code>') . '</p></div>';
787 }
788 if (! is_writable(NFW_LOG_DIR . '/nfwlog') ) {
789 echo '<div class="error notice is-dismissible"><p><strong>' . __('NinjaFirewall error', 'ninjafirewall') . ' :</strong> ' .
790 sprintf( __('%s directory is read-only. Please review your installation and ensure that %s is writable.', 'ninjafirewall'), '<code>'. esc_html(NFW_LOG_DIR) .'/nfwlog/</code>', '<code>/nfwlog/</code>') . '</p></div>';
791 }
792
793 if (! NF_DISABLED) {
794 return;
795 }
796
797 $nfw_options = nfw_get_option('nfw_options');
798 if ( empty($nfw_options['ret_code']) && NF_DISABLED != 11 ) {
799 return;
800 }
801
802 if (! empty($GLOBALS['err_fw'][NF_DISABLED]) ) {
803 $msg = $GLOBALS['err_fw'][NF_DISABLED];
804 } else {
805 $msg = __('unknown error', 'ninjafirewall') . ' #' . NF_DISABLED;
806 }
807 echo '<div class="error notice is-dismissible"><p><strong>' . __('NinjaFirewall fatal error:', 'ninjafirewall') . '</strong> ' . $msg .
808 '. ' . __('Review your installation, your site is not protected.', 'ninjafirewall') . '</p></div>';
809 }
810
811 add_action('admin_head', 'nfw_hide_admin_notices');
812
813 function nfw_hide_admin_notices() {
814 if ( isset( $_GET['page'] ) && ( $_GET['page'] == 'NinjaFirewall' || preg_match('/^nfsub/', $_GET['page'] ) ) ) {
815 remove_all_actions('admin_notices');
816 remove_all_actions('all_admin_notices');
817 }
818 add_action('all_admin_notices', 'nfw_admin_notice');
819 }
820
821 // ---------------------------------------------------------------------
822
823 function nfw_send_loginemail( $user_login, $whoami ) {
824
825 $nfw_options = nfw_get_option('nfw_options');
826
827 if (! empty( $whoami ) ) {
828 $whoami = " ($whoami)";
829 }
830
831 /**
832 * Email notification.
833 */
834 $subject = [];
835 $content = [ $user_login . $whoami, NFW_REMOTE_ADDR,
836 ucfirst( date_i18n('F j, Y @ H:i:s T') ), home_url('/') ];
837
838 NinjaFirewall_mail::send('user_login', $subject, $content, '', [], 1 );
839 }
840
841 // --------------------------------------------------------------------- s1:h0
842
843 function nfw_query( $query ) {
844
845 if (! empty( NinjaFirewall_session::read('nfw_goodguy') ) || nfw_is_whitelisted() ) {
846 return;
847 }
848
849 $nfw_options = nfw_get_option('nfw_options');
850 // Return if not enabled, or if we are accessing the dashboard (e.g., /wp-admin/edit.php):
851 if ( empty($nfw_options['enum_archives']) || empty($nfw_options['enabled']) || is_admin() ) {
852 return;
853 }
854 if ( $query->is_main_query() && $query->is_author() ) {
855 if ( $query->get('author_name') ) {
856 $tmp = 'author_name=' . $query->get('author_name');
857 } elseif ( $query->get('author') ) {
858 $tmp = 'author=' . $query->get('author');
859 } else {
860 $tmp = 'author';
861 }
862 NinjaFirewall_session::delete();
863 $query->set('author_name', '0');
864
865 NinjaFirewall_log::write(
866 "User enumeration scan (author archives)",
867 $tmp,
868 NFWLOG_HIGH, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
869 );
870
871 wp_safe_redirect( home_url('/') );
872 exit;
873 }
874 }
875
876 add_action('pre_get_posts','nfw_query');
877
878 // ---------------------------------------------------------------------
879 add_filter('wp_sitemaps_add_provider', function ($provider, $name) {
880
881 if (! empty( NinjaFirewall_session::read('nfw_goodguy') ) || nfw_is_whitelisted() ) {
882 return $provider;
883 }
884 $nfw_options = nfw_get_option('nfw_options');
885 if ( empty( $nfw_options['enum_sitemap'] ) || empty( $nfw_options['enabled'] ) ) {
886 return $provider;
887 }
888
889 if ( $name == 'users') {
890 return false;
891 }
892 return $provider;
893
894 }, 999, 2);
895 // ---------------------------------------------------------------------
896
897 function nfw_the_author( $display_name ) {
898
899 if (! empty( NinjaFirewall_session::read('nfw_goodguy') ) || nfw_is_whitelisted() ) {
900 return $display_name;
901 }
902 $nfw_options = nfw_get_option('nfw_options');
903 if ( empty( $nfw_options['enum_feed'] ) || empty($nfw_options['enabled']) ) {
904 return $display_name;
905 }
906 if ( is_feed() ) {
907 return '';
908 }
909 return $display_name;
910 }
911
912 add_filter('the_author', 'nfw_the_author', 99999, 1 );
913
914 // ---------------------------------------------------------------------
915
916 function nfw_no_application_passwords() {
917
918 $nfw_options = nfw_get_option('nfw_options');
919 if (! empty( $nfw_options['no_appswd'] ) ) {
920 // We don't log API accesses, only accesses to the script (in firewall.php).
921 return false;
922 }
923 return true;
924 }
925
926 add_filter('wp_is_application_passwords_available', 'nfw_no_application_passwords');
927
928 // --------------------------------------------------------------------- +
929 // REST API access.
930
931 function nfwhook_rest_authentication_errors( $res ) {
932
933 // Whitelisted user?
934 if ( nfw_is_whitelisted() || ! empty( NinjaFirewall_session::read('nfw_goodguy') ) ) {
935 return $res;
936 }
937
938 if (! defined('NF_DISABLED') ) {
939 is_nfw_enabled();
940 }
941 if ( NF_DISABLED ) {
942 return $res;
943 }
944
945 $path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
946 if ( strpos( $path, '/wp-json/wp/v2/pages/') === 0 && current_user_can('edit_pages') ) {
947 return $res;
948 }
949 if ( strpos( $path, '/wp-json/wp/v2/posts/') === 0 && current_user_can('edit_posts') ) {
950 return $res;
951 }
952
953 $nfw_options = nfw_get_option('nfw_options');
954
955 // Allow logged-in users (since 4.5.7)
956 if (! empty( $nfw_options['restapi_loggedin'] ) && is_user_logged_in() ) {
957 return $res;
958 }
959
960 if (! empty( $nfw_options['no_restapi'] ) ) {
961
962 NinjaFirewall_log::write(
963 'WordPress: Blocked access to the WP REST API',
964 $_SERVER['REQUEST_URI'],
965 NFWLOG_HIGH, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
966 );
967
968 return new WP_Error(
969 'nfw_rest_api_access_restricted',
970 esc_html__('Forbidden access', 'ninjafirewall'),
971 ['status' => $nfw_options['ret_code'] ]
972 );
973 }
974 return $res;
975 }
976
977 add_filter('rest_authentication_errors', 'nfwhook_rest_authentication_errors');
978
979 // --------------------------------------------------------------------- s1:h0
980
981 function nfwhook_rest_request_before_callbacks( $res, $hnd, $req ) {
982
983 // Whitelisted user?
984 if ( nfw_is_whitelisted() || ! empty( NinjaFirewall_session::read('nfw_goodguy') ) ) {
985 return $res;
986 }
987
988 if (! defined('NF_DISABLED') ) {
989 is_nfw_enabled();
990 }
991 if ( NF_DISABLED ) { return $res; }
992
993 $nfw_options = nfw_get_option('nfw_options');
994
995 if (! empty( $nfw_options['enum_restapi']) ) {
996
997 if ( strpos( $req->get_route(), '/wp/v2/users') !== false && ! current_user_can('list_users') ) {
998
999 NinjaFirewall_log::write(
1000 'User enumeration scan (REST API)',
1001 $_SERVER['REQUEST_URI'],
1002 NFWLOG_HIGH, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
1003 );
1004
1005 return new WP_Error('nfw_rest_api_access_restricted', __('Forbidden access', 'ninjafirewall'), array('status' => $nfw_options['ret_code']) );
1006 }
1007 }
1008 return $res;
1009 }
1010 add_filter('rest_request_before_callbacks', 'nfwhook_rest_request_before_callbacks', 999, 3);
1011
1012 // ---------------------------------------------------------------------
1013
1014 function nfw_authenticate( $user ) {
1015
1016 $nfw_options = nfw_get_option('nfw_options');
1017
1018 if ( empty( $nfw_options['enum_login']) || empty($nfw_options['enabled']) ) {
1019 return $user;
1020 }
1021
1022 if ( is_wp_error( $user ) ) {
1023 if ( preg_match('/^(?:in(?:correct_password|valid_(?:username|email))|authentication_failed)$/', $user->get_error_code() ) ) {
1024 $lostpass = esc_attr( wp_lostpassword_url() );
1025 $user = new WP_Error('denied',
1026 __('Invalid username, email address or password.', 'ninjafirewall') .
1027 "<br /><a href=\"$lostpass\">".
1028 __('Lost your password?', 'ninjafirewall').
1029 '</a>'
1030 );
1031 add_filter('shake_error_codes', 'nfw_err_shake');
1032 }
1033 }
1034 return $user;
1035 }
1036
1037 add_filter('authenticate', 'nfw_authenticate', 90, 3 );
1038
1039 function nfw_err_shake( $shake_codes ) {
1040 $shake_codes[] = 'denied';
1041 return $shake_codes;
1042 }
1043
1044 // ---------------------------------------------------------------------
1045
1046 function nf_check_dbdata() {
1047
1048 $nfw_options = nfw_get_option('nfw_options');
1049
1050 /**
1051 * Don't do anything if NinjaFirewall is disabled or DB monitoring option is off.
1052 */
1053 if ( empty( $nfw_options['enabled'] ) || empty( $nfw_options['a_51'] ) ) {
1054 return;
1055 }
1056
1057 /**
1058 * Don't run more than once every minute.
1059 */
1060 if ( get_transient('nfw_db_check') !== false ) {
1061 return;
1062 }
1063
1064 /**
1065 * This can be defined in the wp-config.php or .htninja script.
1066 */
1067 if ( defined('NFW_DBCHECK_INTERVAL') ) {
1068 $dbcheck_interval = (int) NFW_DBCHECK_INTERVAL;
1069 if ( $dbcheck_interval < 60 ) {
1070 $dbcheck_interval = 60;
1071 }
1072 } else {
1073 /**
1074 * Default is 60 seconds.
1075 */
1076 $dbcheck_interval = 60;
1077 }
1078
1079 if ( is_multisite() ) {
1080 global $current_blog;
1081 $db_hash = NFW_LOG_DIR .'/nfwlog/cache/db_hash.'. $current_blog->site_id .'-'.
1082 $current_blog->blog_id .'.php';
1083 } else {
1084 global $blog_id;
1085 $db_hash = NFW_LOG_DIR .'/nfwlog/cache/db_hash.'. $blog_id .'.php';
1086 }
1087
1088 $adm_users = nf_get_dbdata();
1089 /**
1090 * Some object caching plugins can return an array with empty keys.
1091 */
1092 if ( empty( $adm_users[0]->user_login ) ) {
1093 set_transient('nfw_db_check', 1, $dbcheck_interval );
1094 return;
1095 }
1096
1097 /**
1098 * Sort by ID to prevent false alerts.
1099 */
1100 usort( $adm_users, 'nfw_sort_by_id');
1101
1102 if (! is_file( $db_hash ) ) {
1103 /**
1104 * We don't have any hash yet, let's create one and quit
1105 * (md5 is faster than sha1 with long strings)
1106 */
1107 @file_put_contents( $db_hash, md5( serialize( $adm_users ) ), LOCK_EX );
1108 set_transient('nfw_db_check', 1, $dbcheck_interval );
1109 return;
1110 }
1111
1112 $old_hash = trim ( file_get_contents( $db_hash ) );
1113 if (! $old_hash ) {
1114 @file_put_contents( $db_hash, md5( serialize( $adm_users ) ), LOCK_EX );
1115 set_transient('nfw_db_check', 1, $dbcheck_interval );
1116 return;
1117 }
1118
1119 /**
1120 * Compare both hashes.
1121 */
1122 if ( $old_hash == md5( serialize( $adm_users ) ) ) {
1123 set_transient('nfw_db_check', 1, $dbcheck_interval );
1124 return;
1125
1126 } else {
1127 /**
1128 * Create or update 60-second transient.
1129 */
1130 set_transient('nfw_db_check', 1, $dbcheck_interval );
1131 /**
1132 * Save the new hash.
1133 */
1134 $tmp = @file_put_contents( $db_hash, md5( serialize( $adm_users ) ), LOCK_EX );
1135 if ( $tmp === FALSE ) {
1136 return;
1137 }
1138
1139 /**
1140 * Retrieve each admin data.
1141 */
1142 $data = '';
1143 foreach( $adm_users as $adm ) {
1144 $data.= "Admin ID: {$adm->ID}\n";
1145 $data.= "-user_login: {$adm->user_login}\n";
1146 $data.= "-user_nicename: {$adm->user_nicename}\n";
1147 $data.= "-user_email: {$adm->user_email}\n";
1148 $data.= "-user_registered: {$adm->user_registered}\n";
1149 $data.= "-display_name: {$adm->display_name}\n\n";
1150 }
1151
1152 /**
1153 * Email notification.
1154 */
1155 $subject = [];
1156 $content = [ home_url('/'), ucfirst( date_i18n('F j, Y @ H:i:s T') ),
1157 count($adm_users), $data ];
1158
1159 NinjaFirewall_mail::send('database_change', $subject, $content, '', [], 1 );
1160
1161 /**
1162 * Log event if required.
1163 */
1164 if (! empty( $nfw_options['a_41'] ) ) {
1165 NinjaFirewall_log::write(
1166 __('Database changes detected', 'ninjafirewall'),
1167 __('administrator account', 'ninjafirewall'),
1168 NFWLOG_POSTDETECT, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
1169 );
1170 }
1171 }
1172 }
1173
1174 // ---------------------------------------------------------------------
1175 // Get admin users (we don't want to use get_users()).
1176
1177 function nf_get_dbdata() {
1178
1179 global $wpdb;
1180 return @$wpdb->get_results(
1181 "SELECT {$wpdb->base_prefix}users.ID,{$wpdb->base_prefix}users.user_login,{$wpdb->base_prefix}users.user_pass,{$wpdb->base_prefix}users.user_nicename,{$wpdb->base_prefix}users.user_email,{$wpdb->base_prefix}users.user_registered,{$wpdb->base_prefix}users.display_name
1182 FROM {$wpdb->base_prefix}users
1183 INNER JOIN {$wpdb->base_prefix}usermeta
1184 ON ( {$wpdb->base_prefix}users.ID = {$wpdb->base_prefix}usermeta.user_id )
1185 WHERE 1=1
1186 AND ( ( ( {$wpdb->base_prefix}usermeta.meta_key = '{$wpdb->prefix}capabilities'
1187 AND {$wpdb->base_prefix}usermeta.meta_value LIKE '%\"administrator\"%') ) )"
1188 );
1189 }
1190
1191 // ---------------------------------------------------------------------
1192
1193 function nfw_sort_by_id( $a, $b ) {
1194
1195 return strcmp( $a->ID, $b->ID );
1196 }
1197
1198 // ---------------------------------------------------------------------
1199
1200 function nfw_get_option( $option ) {
1201
1202 if ( is_multisite() ) {
1203 return get_site_option( $option );
1204 } else {
1205 return get_option( $option );
1206 }
1207 }
1208
1209 // ---------------------------------------------------------------------
1210
1211 function nfw_update_option( $option, $new_value ) {
1212
1213 if ( is_multisite() ) {
1214 update_site_option( $option, $new_value );
1215 }
1216 return update_option( $option, $new_value );
1217 }
1218
1219 // ---------------------------------------------------------------------
1220
1221 function nfw_delete_option( $option ) {
1222
1223 if ( is_multisite() ) {
1224 delete_site_option( $option );
1225 }
1226 return delete_option( $option );
1227 }
1228
1229 // ---------------------------------------------------------------------
1230 // Make sure nfw_options is valid.
1231
1232 function nfw_validate_option( $value ) {
1233
1234 if (! isset( $value['enabled'] ) || ! isset( $value['blocked_msg'] ) ||
1235 ! isset( $value['logo'] ) || ! isset( $value['ret_code'] ) ||
1236 ! isset( $value['scan_protocol'] ) || ! isset( $value['get_scan'] ) ) {
1237
1238 // Data is corrupted:
1239 return false;
1240 }
1241
1242 return true;
1243 }
1244
1245 // ---------------------------------------------------------------------
1246
1247 function nfwhook_update_user_meta( $user_id, $meta_key, $meta_value, $prev_value ) {
1248
1249 nfwhook_user_meta( $meta_key, $meta_value, $prev_value );
1250
1251 }
1252 add_filter('update_user_meta', 'nfwhook_update_user_meta', 1, 4);
1253
1254 // ---------------------------------------------------------------------
1255
1256 function nfwhook_add_user_meta( $user_id, $meta_key, $meta_value ) {
1257
1258 nfwhook_user_meta( $user_id, $meta_key, $meta_value );
1259
1260 }
1261 add_filter('add_user_meta', 'nfwhook_add_user_meta', 1, 3);
1262
1263 // ---------------------------------------------------------------------
1264
1265 function nfwhook_user_meta( $id, $key, $value ) {
1266
1267 if (! defined('NF_DISABLED') ) {
1268 is_nfw_enabled();
1269 }
1270
1271 $nfw_options = nfw_get_option('nfw_options');
1272
1273 /**
1274 * Note: "NFW_DISABLE_PRVESC2" is now deprecated. Use the corresponding
1275 * firewall policy to disable it instead.
1276 */
1277 if ( NF_DISABLED || defined('NFW_DISABLE_PRVESC2') ||
1278 empty( $nfw_options['disallow_privesc'] ) ) {
1279
1280 return;
1281 }
1282
1283 global $wpdb;
1284
1285 if ( is_array( $key ) ) {
1286 $key = serialize( $key );
1287 }
1288
1289 /**
1290 * "current_user_can" must remain here,
1291 * see https://wordpress.org/support/topic/rest-api-problem-2/page/2/#post-11789636
1292 */
1293 if ( preg_match( "/{$wpdb->base_prefix}([0-9]+_)?capabilities/", $key ) &&
1294 ! current_user_can('edit_users') ) {
1295
1296 if ( is_array( $value ) ) {
1297 $value = serialize( $value );
1298 }
1299
1300 if ( strpos( $value, 's:13:"administrator"') === FALSE &&
1301 strpos( $value, 's:6:"editor"') === FALSE &&
1302 strpos( $value, 's:12:"shop_manager"') === FALSE &&
1303 strpos( $value, 's:13:"bbp_keymaster"') === FALSE ) {
1304
1305 return;
1306 }
1307 /**
1308 * If it's a subsite in a network, check what we are supposed to do.
1309 */
1310 if ( is_main_site() !== true && empty( $nfw_options['disallow_privesc_mu'] ) ) {
1311 return;
1312 }
1313
1314 $user_info = get_userdata( $id );
1315 $whoisit = '';
1316 $check_user = [
1317 'subscriber', 'contributor', 'author', 'customer', 'bbp_participant', 'bbp_spectator'
1318 ];
1319 foreach( $user_info->roles as $k => $v ) {
1320 if ( in_array( $v, $check_user ) ) {
1321 $whoisit = $v;
1322 break;
1323 }
1324 }
1325 if ( empty( $whoisit ) && ! empty( $user_info->roles ) ) {
1326 return;
1327 }
1328
1329 if ( strlen( $value ) > 200 ) {
1330 $value = mb_substr( $value, 0, 200, 'utf-8') . '...';
1331 }
1332 $subject = __('Blocked privilege escalation attempt', 'ninjafirewall');
1333
1334 NinjaFirewall_log::write(
1335 "WordPress: $subject",
1336 "$key: $value",
1337 NFWLOG_CRITICAL, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
1338 );
1339
1340 if (! empty( $user_info->user_login ) ) {
1341 $username = "{$user_info->user_login}, ID: $id";
1342 } else {
1343 $usename = '-';
1344 }
1345
1346 /**
1347 * Backtrace.
1348 */
1349 $return = nfw_debug_backtrace( $nfw_options );
1350 if (! empty( $return['nftmpfname'] ) ) {
1351 $attachment = $return['nftmpfname'];
1352 } else {
1353 $attachment = [];
1354 }
1355
1356 /**
1357 * Email notification.
1358 */
1359 $subject = [];
1360 $content = [ home_url('/'), $username, $key, $value, NFW_REMOTE_ADDR,
1361 $_SERVER['SCRIPT_FILENAME'], $_SERVER['REQUEST_URI'],
1362 date_i18n('F j, Y @ H:i:s T') , $return['message'] ];
1363 NinjaFirewall_mail::send('privilege_escalation', $subject, $content, '', $attachment, 1 );
1364
1365 /**
1366 * Block the request.
1367 */
1368 NinjaFirewall_session::delete();
1369 wp_die(
1370 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
1371 'NinjaFirewall: '. __('You are not allowed to perform this task.', 'ninjafirewall'),
1372 $nfw_options['ret_code']
1373 );
1374 }
1375 }
1376
1377 // --------------------------------------------------------------------- s1:h0
1378
1379 function nfw_login_form_hook( $message ) {
1380
1381 if (! empty( NinjaFirewall_session::read('nfw_bfd') ) ) {
1382 return '<p class="message" id="nfw_login_msg">'.
1383 esc_html__('NinjaFirewall brute-force protection is enabled and you are temporarily whitelisted.',
1384 'ninjafirewall') .'</p><br />';
1385 }
1386 return $message;
1387 }
1388 add_filter('login_message', 'nfw_login_form_hook');
1389
1390 // ---------------------------------------------------------------------
1391
1392 function nfw_rate_notice( $nfw_options ) {
1393
1394 // Display a one-time notice after two weeks of use:
1395 $now = time();
1396 if (! empty( $nfw_options['rate_notice'] ) && $nfw_options['rate_notice'] < $now ) {
1397
1398 echo '<div class="notice-info notice is-dismissible"><p>'. sprintf(
1399 __('Hey, it seems that you\'ve been using NinjaFirewall for some time. If you like it, please take <a href="%s">the time to rate it</a>. It took thousand of hours to develop it, but it takes only a couple of minutes to rate it. Thank you!', 'ninjafirewall'),
1400 'https://wordpress.org/support/view/plugin-reviews/ninjafirewall?rate=5#postform'
1401 ) .'</p></div>';
1402
1403 // Clear the reminder flag:
1404 unset( $nfw_options['rate_notice'] );
1405 // Update options:
1406 nfw_update_option('nfw_options', $nfw_options );
1407 }
1408
1409 }
1410
1411 // --------------------------------------------------------------------- s1:h1
1412
1413 function nfw_session_debug() {
1414
1415 // Make sure NinjaFirewall is running :
1416 if (! defined('NF_DISABLED') ) {
1417 is_nfw_enabled();
1418 }
1419 if ( NF_DISABLED ) { return; }
1420
1421 $show_session_icon = 0;
1422 $current_user = wp_get_current_user();
1423 // Check users first:
1424 if ( defined('NFW_SESSION_DEBUG_USER') ) {
1425 $users = explode(',', NFW_SESSION_DEBUG_USER );
1426 foreach ( $users as $user ) {
1427 if ( trim( $user ) == $current_user->user_login ) {
1428 $show_session_icon = 1;
1429 break;
1430 }
1431 }
1432 // Check capabilities:
1433 } elseif ( defined('NFW_SESSION_DEBUG_CAPS') ) {
1434 $caps = explode(',', NFW_SESSION_DEBUG_CAPS );
1435 foreach ( $caps as $cap ) {
1436 if (! empty( $current_user->caps[ trim( $cap ) ] ) ) {
1437 $show_session_icon = 1;
1438 break;
1439 }
1440 }
1441 }
1442
1443 if ( empty( $show_session_icon ) ) { return; }
1444
1445 // Check if the user whitelisted?
1446 if ( empty( NinjaFirewall_session::read('nfw_goodguy') ) ) {
1447 // No
1448 $font = 'ff0000';
1449 } else {
1450 // Yes
1451 $font = '00ff00';
1452 }
1453
1454 global $wp_admin_bar;
1455 $wp_admin_bar->add_menu( array(
1456 'id' => 'nfw_session_dbg',
1457 'title' => "<font color='#{$font}'>NF</font>"
1458 ) );
1459
1460 }
1461
1462 // Check if the session debug option is enabled:
1463 if ( defined('NFW_SESSION_DEBUG_USER') || defined('NFW_SESSION_DEBUG_CAPS') ) {
1464 add_action('admin_bar_menu', 'nfw_session_debug', 500 );
1465 }
1466
1467 // ---------------------------------------------------------------------
1468
1469 function nf_monitor_options( $value, $option, $old_value ) {
1470
1471 // Admin check is done in nfw_load_optmon().
1472
1473 // We're not interested in any object
1474 if ( is_object( $value ) || is_object( $old_value ) ) {
1475 return $value;
1476 }
1477
1478 // Similarly to https://core.trac.wordpress.org/ticket/38903, an integer will
1479 // trigger a DB UPDATE query even if it matches the character stored in the DB
1480 // (e.g.: 0 vs '0'). We must not block that, hence will use '===' only on arrays
1481 // (and that will prevent "Nesting level too deep" error as well):
1482 if ( is_array( $value ) ) {
1483 if ( $value === $old_value ) {
1484 return $value;
1485 }
1486 } else {
1487 // Simple comparison operator for integers and strings:
1488 if ( $value == $old_value ) {
1489 return $value;
1490 }
1491 }
1492
1493 $nfw_options = nfw_get_option('nfw_options');
1494
1495 if ( empty( $nfw_options['enabled'] ) || empty( $nfw_options['disallow_settings'] ) ) {
1496 return $value;
1497 }
1498
1499 // User-defined exclusion list (undocumented), NF options/rules (which are protected
1500 // by the firewall):
1501 if ( ( defined('NFW_OPTMON_EXCLUDE') && strpos( NFW_OPTMON_EXCLUDE, $option ) !== false ) ||
1502 $option === 'nfw_options' || $option === 'nfw_rules') {
1503
1504 return $value;
1505 }
1506
1507 global $wpdb;
1508 $monitor = array(
1509 'admin_email',
1510 'blog_public',
1511 'blogdescription',
1512 'blogname',
1513 'comment_moderation',
1514 'comments_notify',
1515 'comment_registration',
1516 'default_role',
1517 'home',
1518 'mailserver_login',
1519 'siteurl',
1520 'template',
1521 'stylesheet',
1522 'users_can_register'
1523 );
1524
1525 // No changes detected or not what we are looking for:
1526 if (! in_array( $option, $monitor ) ) {
1527 return $value;
1528 }
1529
1530 if ( is_array( $value ) ) {
1531 $tmp = serialize( $value );
1532 $value = '';
1533 if ( strlen( $tmp ) > 200 ) { $tmp = mb_substr( $tmp, 0, 200, 'utf-8') . '...'; }
1534 $value = $tmp;
1535 }
1536 if ( is_array( $old_value ) ) {
1537 $tmp = serialize( $old_value );
1538 $old_value = '';
1539 if ( strlen( $tmp ) > 200 ) { $tmp = mb_substr( $tmp, 0, 200, 'utf-8') . '...'; }
1540 $old_value = $tmp;
1541 }
1542
1543 // Send a notification to the admin:
1544 nf_monitor_options_alert( $option, $value, $old_value, 'settings');
1545
1546 /**
1547 * Log the request.
1548 */
1549 NinjaFirewall_log::write(
1550 'Blocked attempt to modify WordPress settings',
1551 "option: $option, value: $value",
1552 NFWLOG_CRITICAL, 0, $nfw_options, NFW_LOG_DIR .'/nfwlog'
1553 );
1554
1555 // Since 4.0.3 we don't close the connection anymore but
1556 // we block the modification by returning the previous value
1557 return $old_value;
1558 }
1559
1560 // ---------------------------------------------------------------------
1561
1562 function nfw_load_optmon() {
1563
1564 if (! nfw_is_whitelisted() && ! current_user_can('manage_options') ) {
1565 add_filter('pre_update_option', 'nf_monitor_options', 10, 3 );
1566 }
1567 }
1568
1569 add_action('plugins_loaded', 'nfw_load_optmon');
1570
1571 // ---------------------------------------------------------------------
1572 // $type = settings or injection.
1573
1574 function nf_monitor_options_alert( $option, $value, $old_value, $type ) {
1575
1576 $nfw_options = nfw_get_option('nfw_options');
1577
1578 /**
1579 * Backtrace.
1580 */
1581 $return = nfw_debug_backtrace( $nfw_options );
1582 if (! empty( $return['nftmpfname'] ) ) {
1583 $attachment = $return['nftmpfname'];
1584 } else {
1585 $attachment = [];
1586 }
1587
1588 /**
1589 * Email notification.
1590 */
1591 $subject = [];
1592 $content = [ $option, $old_value, $value, home_url('/'), NFW_REMOTE_ADDR,
1593 $_SERVER['SCRIPT_FILENAME'], $_SERVER['REQUEST_URI'],
1594 date_i18n('F j, Y @ H:i:s T'), $return['message'] ];
1595 NinjaFirewall_mail::send('wp_settings', $subject, $content, '', $attachment, 1 );
1596 }
1597
1598 // ---------------------------------------------------------------------
1599 // Display a red notice if there's a pending security update
1600 // in the plugins section.
1601
1602 function nfw_verify_secupdates() {
1603
1604 $nfw_checked = nfw_get_option('nfw_checked');
1605 if ( empty( $nfw_checked['plugins'] ) ) {
1606 return;
1607 }
1608 // Check plugins updates
1609 if (! function_exists('get_plugins') ) {
1610 require_once ABSPATH .'wp-admin/includes/plugin.php';
1611 }
1612
1613 $plugins = get_plugins();
1614 $cleared = 0;
1615 $wp_updates = get_site_transient('update_plugins');
1616
1617 foreach( $plugins as $k => $v ) {
1618 // No name or no version (unlike themes, we're dealing with arrays here)
1619 if ( empty( $v['Name'] ) || empty( $v['Version'] ) ) {
1620 continue;
1621 }
1622
1623 if ( isset( $nfw_checked['plugins'][$k] ) ) {
1624 // Compare current and available versions
1625 if ( version_compare( $v['Version'], $nfw_checked['plugins'][$k]['version'], '<') ) {
1626
1627 $args = [
1628 'name' => $v['Name'],
1629 'plugin' => $k,
1630 'version' => $nfw_checked['plugins'][$k]['version'],
1631 'nonce' => wp_create_nonce('pluginupgrade'),
1632 // We don't display the "Install now" button if WordPress allows the upgrade
1633 'upgrade' => isset( $wp_updates->response[ $k ] ) ? false : true
1634 ];
1635
1636 add_action( "after_plugin_row_{$k}",
1637 function() use ( $args ) {
1638 ?>
1639 <tr class="plugin-update-tr active">
1640 <td colspan="4" class="plugin-update colspanchange">
1641 <div class="update-message notice inline notice-error notice-alt">
1642 <?php
1643 echo esc_html__('Important: NinjaFirewall has detected that this is a security update.', 'ninjafirewall') . ' '.
1644 esc_html__("Don't leave your blog at risk, make sure to update as soon as possible.", 'ninjafirewall');
1645 echo '<br />';
1646 echo '<strong>'. esc_html__('Plugin:', 'ninjafirewall') .'</strong> <em>'.
1647 esc_html( $args['name'] ) .'</em> - <strong>'. esc_html__('New version:', 'ninjafirewall') .'</strong> <em>'.
1648 esc_html( $args['version'] ) .'</em>';
1649
1650 if ( $args['upgrade'] ) {
1651 echo '<p>' . esc_html__('Because this update was released less than 24 hours ago and WordPress.org enforces a mandatory 24-hour update delay on all new plugin releases, NinjaFirewall allows you to update it immediately by clicking the button below.', 'ninjafirewall') .'</p>';
1652 ?>
1653 <button type="button" id="nf-progress-id" class="button button-secondary" onClick="nfwjs_upgrade_plugin('<?php
1654 echo esc_attr( $args['plugin'] ) ?>','<?php
1655 echo esc_attr( $args['version'] ) ?>','<?php
1656 echo esc_attr( $args['nonce'] ) ?>')" />
1657 <?php
1658 echo esc_html__('Update the plugin now!', 'ninjafirewall' )?></button>
1659 &nbsp;&nbsp;&nbsp;
1660 <img style="vertical-align:middle;display:none" id="nf-progress-gif" src="<?php
1661 echo plugins_url('/images/progress.gif', dirname (__FILE__ ) ) ?>" />
1662 <?php
1663 }
1664 echo '<br/><a href="https://blog.nintechnet.com/how-to-get-informed-about-the-latest-security-updates-in-your-wordpress-plugins-and-themes/" target="_blank">' .
1665 esc_html__('More info about this warning', 'ninjafirewall') .'</a>';
1666 ?>
1667 </div>
1668 </td>
1669 </tr>
1670 <?php
1671 }
1672 );
1673
1674 } else {
1675 // Remove if from our cache
1676 unset( $nfw_checked['plugins'][$k] );
1677 $cleared = 1;
1678 }
1679 }
1680 }
1681
1682 // Flush plugins or themes that were uninstalled instead of updated
1683 if (! empty( $nfw_checked['plugins'] ) ) {
1684 foreach( $nfw_checked['plugins'] as $k => $v ) {
1685 if (! file_exists( WP_PLUGIN_DIR ."/$k" ) ) {
1686 unset( $nfw_checked['plugins'][$k] );
1687 $cleared = 1;
1688 }
1689 }
1690 }
1691 if (! empty( $nfw_checked['themes'] ) ) {
1692 foreach( $nfw_checked['themes'] as $k => $v ) {
1693 if (! is_dir( WP_CONTENT_DIR ."/themes/$k" ) ) {
1694 unset( $nfw_checked['themes'][$k] );
1695 $cleared = 1;
1696 }
1697 }
1698 }
1699
1700 // Update our list if needed
1701 if (! empty( $cleared ) ) {
1702 nfw_update_option('nfw_checked', $nfw_checked );
1703 }
1704 }
1705
1706 // ---------------------------------------------------------------------
1707 // Attach a backtrace if required.
1708
1709 function nfw_debug_backtrace( $nfw_options ) {
1710
1711 $return = array();
1712 $return['message'] = '';
1713 $verbosity = nfw_verbosity( $nfw_options );
1714 if ( $verbosity !== false ) {
1715 $return['nftmpfname'] = NFW_LOG_DIR .'/nfwlog/backtrace_'. bin2hex( random_bytes( 8 ) ) .'.txt';
1716 $dbg = debug_backtrace( $verbosity );
1717 array_shift( $dbg );
1718 file_put_contents( $return['nftmpfname'], print_r( $dbg, true ) );
1719 $return['message'] = __('A PHP backtrace has been attached to this message for your convenience.', 'ninjafirewall') . "\n\n";
1720 }
1721 return $return;
1722 }
1723
1724 // ---------------------------------------------------------------------
1725 // Activate WPWAF mode.
1726
1727 function nfw_enable_wpwaf() {
1728
1729 if ( file_exists( WPMU_PLUGIN_DIR .'/'. NINJAFIREWALL_MU_PLUGIN ) ) {
1730 // Quick files comparison. We used md5 as we're only looking for changes,
1731 // i.e., if there was an update.
1732 if ( md5_file( WPMU_PLUGIN_DIR .'/'. NINJAFIREWALL_MU_PLUGIN) === md5_file( __DIR__ .'/loader.php') ) {
1733 return;
1734 }
1735 }
1736
1737 if (! is_dir( WPMU_PLUGIN_DIR ) ) {
1738 if (! @mkdir( WPMU_PLUGIN_DIR, 0755, true ) ) {
1739 return sprintf(
1740 esc_html__('Error, cannot create the %s folder.', 'ninjafirewall') .' '.
1741 esc_html__('Check your server permissions and try again.', 'ninjafirewall'),
1742 esc_html( WPMU_PLUGIN_DIR )
1743 );
1744 }
1745 }
1746
1747 if (! is_writable( WPMU_PLUGIN_DIR ) ) {
1748 return sprintf(
1749 esc_html__('Error, the %s folder is not writable.', 'ninjafirewall') .' '.
1750 esc_html__('Check your server permissions and try again.', 'ninjafirewall'),
1751 esc_html( WPMU_PLUGIN_DIR )
1752 );
1753 }
1754
1755 @copy( __DIR__ .'/loader.php', WPMU_PLUGIN_DIR .'/'. NINJAFIREWALL_MU_PLUGIN);
1756 if (! file_exists( WPMU_PLUGIN_DIR .'/'. NINJAFIREWALL_MU_PLUGIN) ) {
1757 return sprintf(
1758 esc_html__('Error, cannot write %s.', 'ninjafirewall') .' '.
1759 esc_html__('Check your server permissions and try again.', 'ninjafirewall'),
1760 esc_html( WPMU_PLUGIN_DIR .'/'. NINJAFIREWALL_MU_PLUGIN)
1761 );
1762 }
1763
1764 return;
1765 }
1766
1767 // ---------------------------------------------------------------------
1768 // Deactivate WPFAF mode.
1769
1770 function nfw_disable_wpwaf() {
1771
1772 if ( file_exists( WPMU_PLUGIN_DIR .'/'. NINJAFIREWALL_MU_PLUGIN) ) {
1773 unlink( WPMU_PLUGIN_DIR .'/'. NINJAFIREWALL_MU_PLUGIN);
1774 }
1775 }
1776
1777 // ---------------------------------------------------------------------
1778 function nfw_dropins() {
1779
1780 $nfw_options = nfw_get_option('nfw_options');
1781 if ( empty( $nfw_options['enabled'] ) ) { return; }
1782
1783 if ( file_exists( NFW_LOG_DIR .'/nfwlog/dropins.php') ) {
1784 @include_once NFW_LOG_DIR .'/nfwlog/dropins.php';
1785 }
1786 }
1787
1788 add_action('plugins_loaded', 'nfw_dropins', -1);
1789
1790 // ---------------------------------------------------------------------
1791 // For WP <4.9.
1792
1793 if (! function_exists('wp_readonly') ) {
1794 function wp_readonly( $var, $val) {
1795 if ( $var == $val ) {
1796 echo " readonly='readonly'";
1797 }
1798 }
1799 }
1800
1801 // ---------------------------------------------------------------------
1802 // Used to display the toggle/switch's status to screenreaders.
1803 function nfw_aria_label( $var, $val, $text_on, $text_off ) {
1804 if ( $var == $val ) {
1805 echo " aria-label='$text_on'";
1806 } else {
1807 echo " aria-label='$text_off'";
1808 }
1809 }
1810 // ---------------------------------------------------------------------
1811 // EOF
1812