PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.3
Patchstack – WordPress & Plugins Security v2.2.3
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
patchstack / includes / listener.php

listener.php in Patchstack – WordPress & Plugins Security 2.2.3, at includes/listener.php

724 lines 22.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Do not allow the file to be called directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * This class is used to communicate from the API to the plugin.
10 */
11 class P_Listener extends P_Core {
12
13 /**
14 * Add the actions required to hide the login page.
15 *
16 * @param Patchstack $core
17 * @return void
18 */
19 public function __construct( $core ) {
20 parent::__construct( $core );
21
22 // Only hook into the action if the authentication is set and valid.
23 if ( isset( $_POST['webarx_secret'] ) && $this->verifyToken( $_POST['webarx_secret'] ) ) {
24 add_action( 'init', [ $this, 'handleRequest' ] );
25 }
26
27 // OTT action.
28 if ( isset( $_POST['patchstack_ott_action'] ) ) {
29 $ott = get_option( 'patchstack_ott_action', '' );
30 if ( ! empty( $ott ) && hash_equals( $ott, $_POST['patchstack_ott_action'] ) ) {
31 $this->setIpHeader();
32 }
33 }
34 }
35
36 /**
37 * Handle the incoming request.
38 *
39 * @return void
40 */
41 public function handleRequest() {
42 // Loop through all possible actions.
43 foreach ( [
44 'webarx_remote_users' => 'listUsers',
45 'webarx_firewall_switch' => 'switchFirewallStatus',
46 'webarx_wordpress_upgrade' => 'wordpressCoreUpgrade',
47 'webarx_theme_upgrade' => 'themeUpgrade',
48 'webarx_plugins_upgrade' => 'pluginsUpgrade',
49 'webarx_plugins_toggle' => 'pluginsToggle',
50 'webarx_plugins_delete' => 'pluginsDelete',
51 'webarx_get_options' => 'getAvailableOptions',
52 'webarx_set_options' => 'saveOptions',
53 'webarx_refresh_rules' => 'refreshRules',
54 'webarx_get_firewall_bans' => 'getFirewallBans',
55 'webarx_firewall_unban_ip' => 'unbanFirewallIp',
56 'webarx_upload_software' => 'uploadSoftware',
57 'webarx_upload_logs' => 'uploadLogs',
58 'webarx_send_ping' => 'sendPing',
59 'webarx_login_bans' => 'getLoginBans',
60 'webarx_unban_login' => 'unbanLogin',
61 'webarx_debug_info' => 'debugInfo',
62 'webarx_set_ip_header' => 'setIpHeader',
63 'webarx_refresh_license' => 'refreshLicense'
64 ] as $key => $action ) {
65 // Special case for Patchstack plugin upgrade.
66 if ( isset( $_POST[ $key ] ) ) {
67 $this->$action();
68 }
69 }
70 }
71
72 /**
73 * Determine if the provided secret hash equals the sha1 of the private id and key.
74 *
75 * @param string $secret Hash that is sent from our API.
76 * @return boolean
77 */
78 public function verifyToken( $secret ) {
79 $id = get_option( 'patchstack_clientid' );
80 $key = $this->get_secret_key();
81
82 if ( empty( $id ) || empty ( $key ) || strlen( $secret ) != 40 ) {
83 return false;
84 }
85
86 return hash_equals( sha1( $id . $key ), $secret );
87 }
88
89 /**
90 * Determine if given action succeded or not, then return the appropriate message.
91 *
92 * @param mixed $thing
93 * @param string $success
94 * @param string $fail
95 * @return void
96 */
97 private function returnResults( $thing, $success = '', $fail = '' ) {
98 if ( ! is_wp_error( $thing ) && $thing !== false ) {
99 wp_send_json( [ 'success' => $success ] );
100 }
101
102 wp_send_json( [ 'error' => $fail ] );
103 }
104
105 /**
106 * Send a ping back to the API.
107 *
108 * @return void
109 */
110 private function sendPing() {
111 do_action( 'patchstack_send_ping' );
112 wp_send_json( [ 'firewall' => $this->get_option( 'patchstack_basic_firewall' ) == 1 ] );
113 }
114
115 /**
116 * Get list of all users on WordPress
117 *
118 * @return void
119 */
120 private function listUsers() {
121 // Only fetch data we actually need.
122 $users = get_users( [ 'role__in' => [ 'administrator', 'editor', 'author', 'contributor' ] ] );
123 $roles = wp_roles();
124 $roles = $roles->get_names();
125 $data = [];
126
127 // Loop through all users.
128 foreach ( $users as $user ) {
129
130 // Get text friendly version of the role.
131 $text = '';
132 foreach ( $user->roles as $role ) {
133 if ( isset( $roles[ $role ] ) ) {
134 $text .= $roles[ $role ] . ', ';
135 } else {
136 $text .= $role . ', ';
137 }
138 }
139
140 // Push to array that we will eventually output.
141 array_push(
142 $data,
143 [
144 'id' => $user->data->ID,
145 'username' => $user->data->user_login,
146 'email' => $user->data->user_email,
147 'roles' => substr( $text, 0, -2 ),
148 ]
149 );
150 }
151
152 wp_send_json( [ 'users' => $data ] );
153 }
154
155 /**
156 * Switch the firewall status from on to off or off to on.
157 *
158 * @return string
159 */
160 private function switchFirewallStatus() {
161 $state = $this->get_option( 'patchstack_basic_firewall' ) == 1;
162 update_option( 'patchstack_basic_firewall', $state == 1 ? 0 : 1 );
163 $this->returnResults( null, 'Firewall ' . ( $state == 1 ? 'disabled' : 'enabled' ) . '.', null );
164 }
165
166 /**
167 * Upgrade the core of WordPress.
168 *
169 * @return string|void
170 */
171 private function wordpressCoreUpgrade() {
172 @set_time_limit( 180 );
173
174 // Get the core update info.
175 wp_version_check();
176 $core = get_site_transient( 'update_core' );
177
178 // Any updates available?
179 if ( ! isset( $core->updates ) ) {
180 $this->returnResults( false, null, 'No update available at this time.' );
181 }
182
183 // Are we on the latest version already?
184 if ( $core->updates[0]->response == 'latest' ) {
185 $this->returnResults( false, null, 'Site is already running the latest version available.' );
186 }
187
188 // Require some libraries and attempt the upgrade.
189 @include_once ABSPATH . '/wp-admin/includes/admin.php';
190 @include_once ABSPATH . '/wp-admin/includes/class-wp-upgrader.php';
191 $skin = new Automatic_Upgrader_Skin();
192 $upgrader = new Core_Upgrader( $skin );
193 $result = $upgrader->upgrade(
194 $core->updates[0],
195 [
196 'attempt_rollback' => true,
197 'do_rollback' => true,
198 'allow_relaxed_file_ownership' => true,
199 ]
200 );
201 if ( ! $result ) {
202 $this->returnResults( false, null, 'The WordPress core could not be upgraded, most likely because of invalid filesystem connection information.' );
203 }
204
205 // Synchronize again with the API.
206 do_action( 'patchstack_send_software_data' );
207 $this->returnResults( $results, 'WordPress core has been upgraded.' );
208 }
209
210 /**
211 * Upgrade a WordPress theme.
212 *
213 * @return string|void
214 */
215 private function themeUpgrade() {
216 if ( !isset( $_POST['webarx_theme_upgrade'] ) ) {
217 return;
218 }
219
220 @set_time_limit( 180 );
221
222 // Require some files we need to execute the upgrade.
223 $theme = wp_filter_nohtml_kses( $_POST['webarx_theme_upgrade'] );
224 @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
225 if ( file_exists( ABSPATH . 'wp-admin/includes/class-theme-upgrader.php' ) ) {
226 @include_once ABSPATH . 'wp-admin/includes/class-theme-upgrader.php';
227 }
228 @include_once ABSPATH . 'wp-admin/includes/misc.php';
229 @include_once ABSPATH . 'wp-admin/includes/file.php';
230
231 // Upgrade the theme.
232 $skin = new Automatic_Upgrader_Skin();
233 $upgrader = new Theme_Upgrader( $skin );
234 $result = $upgrader->upgrade( $theme, [ 'allow_relaxed_file_ownership' => true ] );
235 if ( ! $result ) {
236 $this->returnResults( false, null, 'The theme could not be upgraded, most likely because of invalid filesystem connection information.' );
237 }
238
239 // Synchronize again with the API.
240 do_action( 'patchstack_send_software_data' );
241 $this->returnResults( null, 'The theme has been updated successfully.' );
242 }
243
244 /**
245 * Upgrade a batch of plugins at once.
246 *
247 * @return string|void
248 */
249 private function pluginsUpgrade() {
250 if (!isset( $_POST['webarx_plugins_upgrade'] ) ) {
251 return;
252 }
253
254 @set_time_limit( 180 );
255
256 // Must have a valid number of plugins received to upgrade.
257 $plugins = wp_filter_nohtml_kses( $_POST['webarx_plugins_upgrade'] );
258 $plugins = explode( '|', $plugins );
259 if ( count( $plugins ) == 0 ) {
260 $this->returnResults( false, null, 'No valid plugin names have been given.' );
261 }
262
263 // Require some files we need to execute the upgrade.
264 @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
265 if ( file_exists( ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php' ) ) {
266 @include_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
267 }
268 @include_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
269
270 @include_once ABSPATH . 'wp-admin/includes/plugin.php';
271 @include_once ABSPATH . 'wp-admin/includes/misc.php';
272 @include_once ABSPATH . 'wp-admin/includes/file.php';
273 @include_once ABSPATH . 'wp-admin/includes/template.php';
274 @wp_update_plugins();
275 $all_plugins = get_plugins();
276
277 // New array with all available plugins and the ones we want to upgrade.
278 $upgrade = [];
279 foreach ( $all_plugins as $path => $data ) {
280 $t = explode( '/', $path );
281 if ( in_array( $t[0], $plugins ) ) {
282 array_push( $upgrade, $path );
283 }
284 }
285
286 // Don't continue if we have no valid plugins to upgrade.
287 if ( count( $upgrade ) == 0 ) {
288 $this->returnResults( false, null, 'No valid plugin names have been given.' );
289 }
290
291 // Upgrade the plugins.
292 $skin = new Automatic_Upgrader_Skin();
293 $upgrader = new Plugin_Upgrader( $skin );
294 $result = $upgrader->bulk_upgrade( $upgrade, [ 'allow_relaxed_file_ownership' => true ] );
295 if ( ! $result ) {
296 $this->returnResults( false, null, 'The plugins could not be upgraded, most likely because of invalid filesystem connection information.' );
297 }
298
299 // Synchronize again with the API.
300 do_action( 'patchstack_send_software_data' );
301 $this->returnResults( null, 'The plugins have been updated successfully.' );
302 }
303
304 /**
305 * Toggle the state of a batch of plugin to activated or de-activated.
306 *
307 * @return string|void
308 */
309 private function pluginsToggle() {
310 if (!isset( $_POST['webarx_plugins'], $_POST['webarx_plugins_toggle'] ) ) {
311 return;
312 }
313
314 @set_time_limit( 180 );
315
316 // Must have a valid number of plugins received to toggle.
317 $plugins = wp_filter_nohtml_kses( $_POST['webarx_plugins'] );
318 $plugins = explode( '|', $plugins );
319 $state = $_POST['webarx_plugins_toggle'] == 'on' ? 'on' : 'off';
320 if ( count( $plugins ) == 0 ) {
321 $this->returnResults( false, null, 'No valid plugin names have been given.' );
322 }
323
324 @include_once ABSPATH . 'wp-admin/includes/plugin.php';
325 $all_plugins = get_plugins();
326
327 // New array with all available plugins and the ones we want to toggle.
328 $toggle = [];
329 foreach ( $all_plugins as $path => $data ) {
330 $t = explode( '/', $path );
331
332 // Don't continue if the plugin does not exist locally.
333 if ( ! in_array( $t[0], $plugins ) ) {
334 continue;
335 }
336
337 // If plugin should be turned on, check if it's already turned on first.
338 if ( $state == 'on' && ! is_plugin_active( $path ) ) {
339 array_push( $toggle, $path );
340 }
341
342 // If plugin should be turned off, check if it's already turned off first.
343 if ( $state == 'off' && is_plugin_active( $path ) ) {
344 array_push( $toggle, $path );
345 }
346 }
347
348 // Don't continue if we have no valid plugins to toggle..
349 if ( count( $toggle ) == 0 ) {
350 $this->returnResults( false, null, 'The plugins are already turned ' . $state . '.' );
351 }
352
353 // Turn the plugins on or off?
354 if ( $state == 'on' ) {
355 activate_plugins( $toggle );
356 }
357
358 if ( $state == 'off' ) {
359 deactivate_plugins( $toggle );
360 }
361
362 // Synchronize again with the API.
363 do_action( 'patchstack_send_software_data' );
364 $this->returnResults( null, 'The ' . ( count( $toggle ) == 1 ? 'plugin has' : 'plugins have' ) . ' been successfully turned ' . $state . '.' );
365 }
366
367 /**
368 * Delete a batch of plugins.
369 *
370 * @return string|void
371 */
372 private function pluginsDelete() {
373 if (!isset( $_POST['webarx_plugins'] ) ) {
374 return;
375 }
376
377 @set_time_limit( 180 );
378
379 // Must have a valid number of plugins received to toggle.
380 $plugins = wp_filter_nohtml_kses( $_POST['webarx_plugins'] );
381 $plugins = explode( '|', $plugins );
382 if ( count( $plugins ) == 0 ) {
383 $this->returnResults( false, null, 'No valid plugin names have been given.' );
384 }
385
386 @include_once ABSPATH . 'wp-admin/includes/file.php';
387 @include_once ABSPATH . 'wp-admin/includes/plugin.php';
388 $all_plugins = get_plugins();
389
390 // New array with all available plugins and the ones we want to toggle.
391 $delete = [];
392 foreach ( $all_plugins as $path => $data ) {
393 $t = explode( '/', $path );
394
395 // Don't continue if the plugin does not exist locally.
396 if ( ! in_array( $t[0], $plugins ) ) {
397 continue;
398 }
399
400 array_push( $delete, $path );
401 }
402
403 // Don't continue if we have no valid plugins to toggle..
404 if ( count( $delete ) == 0 ) {
405 $this->returnResults( false, null, 'No valid plugins to delete.' );
406 }
407
408 @deactivate_plugins( $delete );
409 @delete_plugins( $delete );
410
411 // Synchronize again with the API.
412 do_action( 'patchstack_send_software_data' );
413 $this->returnResults( null, 'The plugins have been successfully deleted.' );
414 }
415
416 /**
417 * Save received options.
418 *
419 * @return void
420 */
421 private function saveOptions() {
422 if ( ! isset( $_POST['webarx_set_options'], $_POST['webarx_secret'] ) ) {
423 exit;
424 }
425
426 // Get the received options.
427 $options = json_decode( base64_decode( $_POST['webarx_set_options'] ), true );
428 if ( ! $options || count( $options ) == 0 ) {
429 exit;
430 }
431
432 // Loop through the options and update their value.
433 $exclude_filter = ['patchstack_firewall_custom_rules'];
434 foreach ( $options as $key => $value ) {
435 if ( array_key_exists( $key, $this->plugin->admin_options->options ) ) {
436
437 // Some options should not be filtered and could cause unexpected behavior if they are filtered.
438 if ( ! in_array( $key, $exclude_filter ) ) {
439 $value = map_deep( $value, 'wp_filter_nohtml_kses' );
440 }
441
442 update_option( $key, $value, true );
443 }
444 }
445
446 $this->returnResults( null, 'Plugin options has been updated.' );
447 }
448
449 /**
450 * Return list of keys and values of Patchstack options.
451 *
452 * @return array
453 */
454 private function getAvailableOptions() {
455 // Get all options and filter by the Patchstack prefix.
456 global $wpdb;
457 $options = $wpdb->get_results( "SELECT option_name, option_value FROM " . $wpdb->options . " WHERE option_name LIKE 'patchstack_%'" );
458 $settings = [];
459 $found = [];
460 foreach ( $options as $option ) {
461 array_push( $found, $option->option_name );
462 $settings[] = (array) $option;
463 }
464
465 // Check for potential missing options and add them to the output.
466 foreach( [ 'patchstack_firewall_custom_rules' ] as $slug ) {
467 if ( ! isset ( $found[$slug] ) ) {
468 $settings[] = [
469 'option_name' => $slug,
470 'option_value' => $this->get_option( $slug, '' )
471 ];
472 }
473 }
474
475 // Add custom values which aren't directly available from the options table.
476 // User roles available for whitelisting.
477 $roles = wp_roles();
478 $roles = $roles->get_names();
479 $roles_available = [];
480 foreach ( $roles as $key => $role ) {
481 $roles_available[ $key ] = $role;
482 }
483 $settings[] = [
484 'option_name' => 'patchstack_basic_firewall_roles_available',
485 'option_value' => serialize( $roles_available ),
486 ];
487
488 // Whether or not auto-updates are disabled in the code.
489 $settings[] = [
490 'option_name' => 'patchstack_auto_updates_disabled',
491 'option_value' => defined( 'AUTOMATIC_UPDATER_DISABLED' ) && AUTOMATIC_UPDATER_DISABLED,
492 ];
493
494 wp_send_json( $settings );
495 }
496
497 /**
498 * Pull firewall rules from the API.
499 *
500 * @return void
501 */
502 private function refreshRules() {
503 do_action( 'patchstack_post_dynamic_firewall_rules' );
504 $this->returnResults( null, 'Firewall rules have been refreshed.' );
505 }
506
507 /**
508 * Get a list of IP addresses that are currently banned by the firewall.
509 *
510 * @return void
511 */
512 private function getFirewallBans() {
513 // Calculate block time.
514 $minutes = (int) $this->get_option( 'patchstack_autoblock_minutes', 30 );
515 $timeout = (int) $this->get_option( 'patchstack_autoblock_blocktime', 60 );
516 if ( empty( $minutes ) || empty( $timeout ) ) {
517 $time = 30 + 60;
518 } else {
519 $time = $minutes + $timeout;
520 }
521
522 global $wpdb;
523 $results = $wpdb->get_results(
524 $wpdb->prepare( 'SELECT ip FROM ' . $wpdb->prefix . "patchstack_firewall_log WHERE apply_ban = 1 AND log_date >= ('" . current_time( 'mysql' ) . "' - INTERVAL %d MINUTE) GROUP BY ip", [ $time ] ),
525 OBJECT
526 );
527
528 $out = [];
529 foreach ( $results as $result ) {
530 if ( isset( $result->ip ) ) {
531 array_push( $out, $result->ip );
532 }
533 }
534
535 wp_send_json( $out );
536 }
537
538 /**
539 * Unban a specific IP address from the firewall.
540 *
541 * @return void
542 */
543 private function unbanFirewallIp() {
544 if ( ! isset( $_POST['webarx_ip'] ) || !filter_var( $_POST['webarx_ip'], FILTER_VALIDATE_IP ) ) {
545 return;
546 }
547
548 global $wpdb;
549 $wpdb->query( $wpdb->prepare( 'UPDATE ' . $wpdb->prefix . 'patchstack_firewall_log SET apply_ban = 0 WHERE ip = %s', [ $_POST['webarx_ip'] ] ) );
550 $this->returnResults( null, 'The IP has been unbanned.' );
551 }
552
553 /**
554 * Send all current software on the WordPress site to the API.
555 *
556 * @return void
557 */
558 private function uploadSoftware() {
559 do_action( 'patchstack_send_software_data' );
560 $this->returnResults( null, 'The software data has been sent to the API.' );
561 }
562
563 /**
564 * Upload the firewall and activity logs.
565 *
566 * @return void
567 */
568 private function uploadLogs() {
569 do_action( 'patchstack_send_hacker_logs' );
570 do_action( 'patchstack_send_event_logs' );
571 $this->returnResults( null, 'The logs have been sent to the API.' );
572 }
573
574 /**
575 * Get the currently banned IP addresses from the login page.
576 *
577 * @return void
578 */
579 private function getLoginBans() {
580 // Calculate block time.
581 $minutes = (int) $this->get_option( 'patchstack_anti_bruteforce_minutes', 30 );
582 $timeout = (int) $this->get_option( 'patchstack_anti_bruteforce_blocktime', 60 );
583 if ( empty( $minutes ) || empty( $timeout ) ) {
584 $time = 30 + 60;
585 } else {
586 $time = $minutes + $timeout;
587 }
588
589 // Check if X failed login attempts were made.
590 global $wpdb;
591 $results = $wpdb->get_results(
592 $wpdb->prepare( 'SELECT id, ip, date FROM ' . $wpdb->prefix . "patchstack_event_log WHERE action = 'failed login' AND date >= ('" . current_time( 'mysql' ) . "' - INTERVAL %d MINUTE) GROUP BY ip HAVING COUNT(ip) >= %d ORDER BY date DESC", [ $time, $this->get_option( 'patchstack_anti_bruteforce_attempts', 10 ) ] ),
593 OBJECT
594 );
595
596 // Return the banned IP addresses.
597 wp_send_json( [ 'banned' => $results ] );
598 }
599
600 /**
601 * Unban a banned login IP address.
602 *
603 * @return void
604 */
605 private function unbanLogin() {
606 if ( ! isset( $_POST['id'], $_POST['type'] ) || !ctype_digit( $_POST['id'] ) ) {
607 exit;
608 }
609
610 global $wpdb;
611
612 // Unblock the IP; delete the logs of the IP.
613 if ( $_POST['type'] == 'unblock' ) {
614 // First get the IP address to unblock.
615 $result = $wpdb->get_results(
616 $wpdb->prepare( 'SELECT ip FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id = %d', [ (int) $_POST['id'] ] )
617 );
618
619 // Unblock the IP address.
620 if ( isset( $result[0], $result[0]->ip ) && filter_var( $result[0]->ip, FILTER_VALIDATE_IP ) ) {
621 $wpdb->query(
622 $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE ip = %s', [ $result[0]->ip ] )
623 );
624 }
625 }
626
627 // Unblock and whitelist the IP.
628 if ( $_POST['type'] == 'unblock_whitelist' ) {
629 // First get the IP address to whitelist.
630 $result = $wpdb->get_results(
631 $wpdb->prepare( 'SELECT ip FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id = %d', [ (int) $_POST['id'] ] )
632 );
633
634 // Whitelist and unblock the IP address.
635 if ( isset( $result[0], $result[0]->ip ) && filter_var( $result[0]->ip, FILTER_VALIDATE_IP ) ) {
636 update_option( 'patchstack_login_whitelist', $this->get_option( 'patchstack_login_whitelist', '' ) . "\n" . $result[0]->ip );
637 $wpdb->query(
638 $wpdb->prepare( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE ip = %s', [ $result[0]->ip ] )
639 );
640 }
641 }
642
643 $this->returnResults( null, 'The unban has been processed.' );
644 }
645
646 /**
647 * Get information for debugging purposes.
648 *
649 * @return void
650 */
651 private function debugInfo() {
652 $debug = [
653 'server' => $_SERVER,
654 'php' => phpversion()
655 ];
656
657 wp_send_json( $debug );
658 }
659
660 /**
661 * Try to determine the proper IP address headers.
662 *
663 * @return void
664 */
665 private function setIpHeader() {
666 if ( ! isset( $_POST['ip'] ) ) {
667 return;
668 }
669
670 $ips = ! is_array ( $_POST['ip'] ) ? [ $_POST['ip'] ] : $_POST['ip'];
671
672 // REMOTE_ADDR?
673 foreach ( $ips as $ip ) {
674 if ( isset( $_SERVER['REMOTE_ADDR'] ) && $_SERVER['REMOTE_ADDR'] == $ip ) {
675 update_option( 'patchstack_firewall_ip_header', 'REMOTE_ADDR' );
676 update_option( 'patchstack_ip_header_computed', 1 );
677 update_option( 'patchstack_ott_action', '' );
678 wp_send_json( [ 'success' => true, 'header' => 'REMOTE_ADDR' ] );
679 }
680 }
681
682 // IP address headers in order of priority.
683 $priority = [ 'REMOTE_ADDR', 'HTTP_CF_CONNECTING_IP', 'HTTP_X_SUCURI_CLIENTIP', 'HTTP_X_REAL_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'SUCURI_RIP' ];
684 foreach ( $ips as $ip ) {
685 foreach ( $priority as $header ) {
686 if ( isset( $_SERVER[ $header ] ) && $_SERVER[ $header ] == $ip ) {
687 update_option( 'patchstack_firewall_ip_header', $header );
688 update_option( 'patchstack_ip_header_computed', 1 );
689 update_option( 'patchstack_ott_action', '' );
690 wp_send_json( [ 'success' => true, 'header' => $header ] );
691 }
692 }
693 }
694
695 // Still not found? Iterate over all $_SERVER keys.
696 foreach ( $ips as $ip ) {
697 foreach ( $_SERVER as $key => $value ) {
698 if ( $value == $ip ) {
699 update_option( 'patchstack_firewall_ip_header', $key );
700 update_option( 'patchstack_ip_header_computed', 1 );
701 update_option( 'patchstack_ott_action', '' );
702 wp_send_json( [ 'success' => true, 'header' => $key ] );
703 }
704 }
705 }
706
707 update_option( 'patchstack_ott_action', '' );
708 wp_send_json( [ 'success' => false, 'header' => 'unknown' ] );
709 }
710
711 /**
712 * Refresh the license and subscription information.
713 *
714 * @return void
715 */
716 private function refreshLicense (){
717 do_action( 'update_license_status' );
718 do_action( 'patchstack_send_software_data' );
719 do_action( 'patchstack_post_dynamic_firewall_rules' );
720
721 wp_send_json( array( 'success' => true ) );
722 }
723 }
724