PluginProbe
Patchstack – WordPress & Plugins Security / 2.1.22
Patchstack – WordPress & Plugins Security v2.1.22
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.1.22, at includes/listener.php

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