PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.6.0
Defender Security – Malware Scanner, Login Security & Firewall v5.6.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / class-smart-ip-detection.php

class-smart-ip-detection.php in Defender Security – Malware Scanner, Login Security & Firewall 5.6.0, at src/component/class-smart-ip-detection.php

275 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles the functionality for detecting the IP header to be used on the site.
4 *
5 * @package WP_Defender\Component
6 */
7
8 namespace WP_Defender\Component;
9
10 use WP_Defender\Component;
11 use WP_Defender\Controller\Firewall;
12 use WP_Defender\Model\Setting\Firewall as Model_Firewall;
13 use WP_Error;
14
15 /**
16 * Handles the functionality for detecting the IP header to be used on the site.
17 *
18 * @since 4.9.0
19 */
20 class Smart_Ip_Detection extends Component {
21 /**
22 * The version of the API.
23 *
24 * @var string
25 */
26 public const API_VERSION = '1';
27
28 /**
29 * The namespace of the API.
30 *
31 * @var string
32 */
33 public const API_NAMESPACE = 'wpdef';
34
35 /**
36 * The route of the API.
37 *
38 * @var string
39 */
40 public const API_ROUTE = 'detect-ip-header';
41
42 /**
43 * The IP header detected by Smart IP Detection.
44 *
45 * @var string
46 */
47 public const SMART_IP_DETECTION_HEADER = 'wd_smart_ip_detection_header';
48
49 /**
50 * The list of IP services.
51 *
52 * @var array
53 */
54 public const IP_SERVICES = array(
55 'https://api.ipify.org',
56 'https://checkip.amazonaws.com',
57 'https://ipinfo.io/ip',
58 );
59
60 /**
61 * AJAX action for the ping.
62 *
63 * @var string
64 */
65 public const ACTION_PING = 'defender_smart_ip_detection_ping';
66
67 /**
68 * Get namespace with version.
69 *
70 * @return string
71 */
72 public static function get_namespace(): string {
73 return self::API_NAMESPACE . '/v' . self::API_VERSION;
74 }
75
76 /**
77 * Get the route of the API.
78 *
79 * @return string
80 */
81 public static function get_route(): string {
82 return '/' . self::API_ROUTE;
83 }
84
85 /**
86 * Check if the IP detection is set to automatic.
87 *
88 * @return bool True if the IP detection is set to automatic, false otherwise.
89 */
90 public function is_smart_ip_detection_enabled(): bool {
91 $model = wd_di()->get( Model_Firewall::class );
92 return 'automatic' === $model->ip_detection_type;
93 }
94
95 /**
96 * Detect the IP header based on the known IP(s).
97 *
98 * @return array|WP_Error The detected IP & header, or an error message if the header is not found.
99 */
100 public function smart_ip_detect_header() {
101 $headers_to_check = array(
102 'HTTP_CF_CONNECTING_IP',
103 'HTTP_X_REAL_IP',
104 'REMOTE_ADDR',
105 'HTTP_X_FORWARDED_FOR',
106 );
107
108 $known_ips = array(
109 $this->get_server_public_ip(),
110 '127.0.0.1',
111 );
112
113 $server_addr = defender_get_data_from_request( 'SERVER_ADDR', 's' );
114 if ( $this->validate_ip( $server_addr ) ) {
115 $known_ips[] = $server_addr;
116 }
117 $known_ips = array_unique( $known_ips );
118
119 foreach ( $headers_to_check as $header ) {
120 $ips = defender_get_data_from_request( $header, 's' );
121 if ( ! empty( $ips ) ) {
122 $ips = array_map( 'trim', preg_split( '/[\s,]+/', $ips ) );
123 foreach ( $ips as $ip ) {
124 if ( in_array( $ip, $known_ips, true ) ) {
125 update_site_option( self::SMART_IP_DETECTION_HEADER, $header );
126
127 $msg = sprintf(
128 /* translators: 1. IP header. 2. IP. */
129 esc_html__( 'IP header detected: %1$s with IP: %2$s.', 'defender-security' ),
130 $header,
131 $ip
132 );
133 $this->log( 'Smart IP DETECTION: ' . $msg, Firewall::FIREWALL_LOG );
134 return array(
135 'header' => $header,
136 'ip' => $ip,
137 'message' => $msg,
138 );
139 }
140 }
141 }
142 }
143
144 $msg = sprintf(
145 /* translators: 1. IP(s) */
146 esc_html__( 'Not able to find any IP header via IP(s): %s.', 'defender-security' ),
147 implode( ',', $known_ips )
148 );
149 $this->log( 'Smart IP DETECTION: ' . $msg, Firewall::FIREWALL_LOG );
150 return new WP_Error(
151 'defender_smart_ip_detection_no_header',
152 $msg
153 );
154 }
155
156 /**
157 * Send request to the API endpoint for IP detection.
158 *
159 * @param bool $is_skipped Whether to skip the check if Smart IP Detection is enabled.
160 *
161 * @return bool True if the request is sent successfully, false otherwise.
162 */
163 public function smart_ip_detection_ping( bool $is_skipped = false ): bool {
164 if ( ! $is_skipped && ! $this->is_smart_ip_detection_enabled() ) {
165 $this->log( 'Smart IP DETECTION: IP Detection is not set to \'automatic\'', Firewall::FIREWALL_LOG );
166 return false;
167 }
168
169 $nonce = bin2hex( Crypt::random_bytes( 32 ) );
170 set_transient( self::get_nonce_context(), $nonce, MINUTE_IN_SECONDS );
171
172 $url = add_query_arg(
173 array(
174 'action' => self::ACTION_PING,
175 'nonce' => $nonce,
176 ),
177 admin_url( 'admin-ajax.php' )
178 );
179 $args = array(
180 'sslverify' => false, // Many hosts have no updated CA bundle.
181 );
182 $response = wp_remote_get( $url, $args );
183
184 if ( is_wp_error( $response ) ) {
185 $this->log( 'Smart IP DETECTION: ' . $response->get_error_message(), Firewall::FIREWALL_LOG );
186 return false;
187 } elseif ( 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
188 $this->log( 'Smart IP DETECTION: ' . wp_remote_retrieve_response_message( $response ), Firewall::FIREWALL_LOG );
189 return false;
190 } else {
191 $this->log( 'Smart IP DETECTION: Pinged successfully.', Firewall::FIREWALL_LOG );
192 return true;
193 }
194 }
195
196 /**
197 * Get the details of the Smart IP Detection.
198 *
199 * @return array The details of the IP and header.
200 */
201 public function get_smart_ip_detection_details(): array {
202 $ip_details = array();
203 $recommended_header = get_site_option( self::SMART_IP_DETECTION_HEADER );
204
205 $server = defender_get_data_from_request( null, 's' );
206 if ( isset( $server[ $recommended_header ] ) ) {
207 $ip_details[] = array( $server[ $recommended_header ], $recommended_header );
208 }
209
210 $ip_details[] = array_key_exists( 'REMOTE_ADDR', $server )
211 ? array( $server['REMOTE_ADDR'], 'REMOTE_ADDR' )
212 : array( '127.0.0.1', 'REMOTE_ADDR' );
213
214 if ( isset( $server['HTTP_X_FORWARDED_FOR'] ) ) {
215 $ip_details[] = array( $server['HTTP_X_FORWARDED_FOR'], 'HTTP_X_FORWARDED_FOR' );
216 }
217
218 if ( isset( $server['HTTP_X_REAL_IP'] ) ) {
219 $ip_details[] = array( $server['HTTP_X_REAL_IP'], 'HTTP_X_REAL_IP' );
220 }
221
222 $private_ip = array();
223 foreach ( $ip_details as $ip_detail ) {
224 $ip_array = array_reverse( array_map( 'trim', preg_split( '/[\s,]+/', $ip_detail[0] ) ) );
225
226 foreach ( $ip_array as $ip ) {
227 if ( ! $this->is_private_ip( $ip ) ) {
228 // Immediately return the first public IP found.
229 return array( $ip, $ip_detail[1] );
230 } else {
231 $private_ip[] = array( $ip, $ip_detail[1] );
232 }
233 }
234 }
235
236 // If public IP was not found then return the first private IP.
237 return isset( $private_ip[0] ) ? $private_ip[0] : array();
238 }
239
240 /**
241 * Remove the IP header detected by Smart IP Detection.
242 */
243 public static function remove_header(): void {
244 delete_site_option( self::SMART_IP_DETECTION_HEADER );
245 }
246
247 /**
248 * Get server's public IP from various sources.
249 *
250 * @return string
251 */
252 public function get_server_public_ip(): string {
253 $ip = '';
254
255 foreach ( self::IP_SERVICES as $service ) {
256 $response = wp_remote_get( $service );
257 if ( ! is_wp_error( $response ) ) {
258 $ip = trim( wp_remote_retrieve_body( $response ) );
259 if ( $this->validate_ip( $ip ) ) {
260 return $ip;
261 }
262 }
263 }
264
265 return $ip;
266 }
267
268 /**
269 * Get the nonce context for the AJAX action.
270 */
271 public static function get_nonce_context(): string {
272 return self::ACTION_PING . '_ajax_nonce';
273 }
274 }
275