PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.1.0
Defender Security – Malware Scanner, Login Security & Firewall v3.1.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 / traits / ip.php

ip.php in Defender Security – Malware Scanner, Login Security & Firewall 3.1.0, at src/traits/ip.php

475 lines 11.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WP_Defender\Traits;
4
5 use WP_Defender\Extra\IP_Helper;
6
7 trait IP {
8 /**
9 * @param $ip
10 *
11 * @return mixed
12 */
13 private function is_v4( $ip ) {
14 return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
15 }
16
17 /**
18 * @param $ip
19 *
20 * @return mixed
21 */
22 private function is_v6( $ip ) {
23 return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
24 }
25
26 /**
27 * @return bool
28 */
29 private function is_v6_support() {
30 return defined( 'AF_INET6' );
31 }
32
33 /**
34 * @param $ip
35 *
36 * @return bool|string
37 */
38 private function expand_ip_v6( $ip ) {
39 $hex = unpack( 'H*hex', inet_pton( $ip ) );
40 $ip = substr( preg_replace( '/([A-f0-9]{4})/', '$1:', $hex['hex'] ), 0, - 1 );
41
42 return $ip;
43 }
44
45 /**
46 * @param $inet
47 *
48 * @src https://stackoverflow.com/a/7951507
49 * @return string
50 */
51 private function ine_to_bits( $inet ) {
52 $unpacked = unpack( 'a16', $inet );
53 $unpacked = str_split( $unpacked[1] );
54 $binaryip = '';
55 foreach ( $unpacked as $char ) {
56 $binaryip .= str_pad( decbin( ord( $char ) ), 8, '0', STR_PAD_LEFT );
57 }
58
59 return $binaryip;
60 }
61
62 /**
63 * @param $ip
64 * @param $first_in_range
65 * @param $last_in_range
66 *
67 * @return bool
68 */
69 private function compare_v4_in_range( $ip, $first_in_range, $last_in_range ) {
70 $low = sprintf( '%u', ip2long( $first_in_range ) );
71 $high = sprintf( '%u', ip2long( $last_in_range ) );
72
73 $cip = sprintf( '%u', ip2long( $ip ) );
74 if ( $high >= $cip && $cip >= $low ) {
75 return true;
76 }
77
78 return false;
79 }
80
81 /**
82 * @param $ip
83 * @param $first_in_range
84 * @param $last_in_range
85 *
86 * @return bool
87 */
88 private function compare_v6_in_range( $ip, $first_in_range, $last_in_range ) {
89 $first_in_range = inet_pton( $this->expand_ip_v6( $first_in_range ) );
90 $last_in_range = inet_pton( $this->expand_ip_v6( $last_in_range ) );
91 $ip = inet_pton( $this->expand_ip_v6( $ip ) );
92
93 if ( ( strlen( $ip ) === strlen( $first_in_range ) )
94 && ( $ip >= $first_in_range && $ip <= $last_in_range ) ) {
95 return true;
96 } else {
97 return false;
98 }
99 }
100
101 /**
102 * @param $ip
103 * @param $block
104 *
105 * @src http://stackoverflow.com/a/594134
106 * @return bool
107 */
108 private function compare_cidrv4( $ip, $block ) {
109 [$subnet, $bits] = explode( '/', $block );
110 if ( is_null( $bits ) ) {
111 $bits = 32;
112 }
113 $ip = ip2long( $ip );
114 $subnet = ip2long( $subnet );
115 $mask = - 1 << ( 32 - $bits );
116 $subnet &= $mask;// nb: in case the supplied subnet wasn't correctly aligned
117
118 return ( $ip & $mask ) == $subnet;// phpcs:ignore
119 }
120
121 /**
122 * @param $ip
123 * @param $block
124 *
125 * @return bool
126 */
127 private function compare_cidrv6( $ip, $block ) {
128 $ip = $this->expand_ip_v6( $ip );
129 $ip = inet_pton( $ip );
130 $b_ip = $this->ine_to_bits( $ip );
131 [$subnet, $bits] = explode( '/', $block );
132 $subnet = $this->expand_ip_v6( $subnet );
133 $subnet = inet_pton( $subnet );
134 $b_subnet = $this->ine_to_bits( $subnet );
135
136 $ip_net_bits = substr( $b_ip, 0, $bits );
137 $subnet_bits = substr( $b_subnet, 0, $bits );
138
139 return $ip_net_bits === $subnet_bits;
140 }
141
142 /**
143 * Compare ip2 to ip1, true if ip2>ip1, false if not.
144 *
145 * @param $ip1
146 * @param $ip2
147 *
148 * @return bool
149 */
150 public function compare_ip( $ip1, $ip2 ) {
151 if ( $this->is_v4( $ip1 ) && $this->is_v4( $ip2 ) ) {
152 if ( sprintf( '%u', ip2long( $ip2 ) ) - sprintf( '%u', ip2long( $ip1 ) ) > 0 ) {
153 return true;
154 }
155 } elseif ( $this->is_v6( $ip1 ) && $this->is_v6( $ip2 ) && $this->is_v6_support() ) {
156 $ip1 = inet_pton( $this->expand_ip_v6( $ip1 ) );
157 $ip2 = inet_pton( $this->expand_ip_v6( $ip2 ) );
158
159 return $ip2 > $ip1;
160 }
161
162 return false;
163 }
164
165 /**
166 * @param $ip
167 * @param $first_in_range
168 * @param $last_in_range
169 *
170 * @return bool
171 */
172 public function compare_in_range( $ip, $first_in_range, $last_in_range ) {
173 if ( $this->is_v4( $first_in_range ) && $this->is_v4( $last_in_range ) ) {
174 return $this->compare_v4_in_range( $ip, $first_in_range, $last_in_range );
175 } elseif ( $this->is_v6( $first_in_range ) && $this->is_v6( $last_in_range ) && $this->is_v6_support() ) {
176 $this->compare_v6_in_range( $ip, $first_in_range, $last_in_range );
177 }
178
179 return false;
180 }
181
182 public function compare_cidr( $ip, $block ) {
183 [$subnet, $bits] = explode( '/', $block );
184 if ( $this->is_v4( $ip ) && $this->is_v4( $subnet ) ) {
185 return $this->compare_cidrv4( $ip, $block );
186 } elseif ( $this->is_v6( $ip ) && $this->is_v6( $subnet ) && $this->is_v6_support() ) {
187 return $this->compare_cidrv6( $ip, $block );
188 }
189
190 return false;
191 }
192
193 /**
194 * $ip an be single ip, or a range like xxx.xxx.xxx.xxx - xxx.xxx.xxx.xxx or CIDR.
195 * @param $ip
196 *
197 * @return bool
198 */
199 public function validate_ip( $ip ) {
200 if (
201 ! stristr( $ip, '-' )
202 && ! stristr( $ip, '/' )
203 && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
204 // Only ip, no '-', '/' symbols.
205 return true;
206 } elseif ( stristr( $ip, '-' ) ) {
207 $ips = explode( '-', $ip );
208 foreach ( $ips as $ip_key ) {
209 if ( ! filter_var( $ip_key, FILTER_VALIDATE_IP ) ) {
210 return false;
211 }
212 }
213 if ( $this->compare_ip( $ips[0], $ips[1] ) ) {
214 return true;
215 }
216 } elseif ( stristr( $ip, '/' ) ) {
217 [$ip, $bits] = explode( '/', $ip );
218 if ( filter_var( $ip, FILTER_VALIDATE_IP ) && filter_var( $bits, FILTER_VALIDATE_INT ) ) {
219 if ( $this->is_v4( $ip ) && 0 <= $bits && $bits <= 32 ) {
220 return true;
221 } elseif ( $this->is_v6( $ip ) && 0 <= $bits && $bits <= 128 && $this->is_v6_support() ) {
222 return true;
223 }
224 }
225 }
226
227 return false;
228 }
229
230 /**
231 * Display a message if IP is non-valid. The cases:
232 * 1) single IP (no '-', '/' symbols),
233 * 2) IP range,
234 * 3) CIDR.
235 * Ignore cases with private, reserved ranges.
236 * @src https://en.wikipedia.org/wiki/IPv4#Special-use_addresses
237 * @src https://en.wikipedia.org/wiki/IPv6#Special-use_addresses
238 * @param $ip
239 *
240 * @return array
241 */
242 public function display_validation_message( $ip ) {
243 $errors = array();
244 // Case1: single IP.
245 if (
246 ! stristr( $ip, '-' )
247 && ! stristr( $ip, '/' )
248 && ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
249 $errors[] = sprintf(
250 /* translators: ... */
251 __( '<b>%s</b> – invalid format', 'wpdef' ),
252 $ip
253 );
254 // Case2: IP range.
255 } elseif ( stristr( $ip, '-' ) ) {
256 $ips = explode( '-', $ip );
257 foreach ( $ips as $ip_key ) {
258 if ( ! filter_var( $ip_key, FILTER_VALIDATE_IP ) ) {
259 $errors[] = sprintf(
260 /* translators: ... */
261 __( '<b>%s</b> – invalid format', 'wpdef' ),
262 $ip_key
263 );
264 }
265 }
266 if ( ! $this->compare_ip( $ips[0], $ips[1] ) ) {
267 $errors[] = sprintf(
268 /* translators: ... */
269 __( "Can't compare <b>%s</b> with <b>%s</b>.", 'wpdef' ),
270 $ips[1],
271 $ips[0]
272 );
273 }
274 // Case3: CIDR.
275 } elseif ( stristr( $ip, '/' ) ) {
276 [$ip, $bits] = explode( '/', $ip );
277 if ( filter_var( $ip, FILTER_VALIDATE_IP ) && filter_var( $bits, FILTER_VALIDATE_INT ) ) {
278 if ( $this->is_v4( $ip ) && 0 <= $bits && $bits <= 32 ) {
279 // IPv4 is correct.
280 } elseif ( $this->is_v6( $ip ) && 0 <= $bits && $bits <= 128 && $this->is_v6_support() ) {
281 // IPv6 is correct.
282 } else {
283 $errors[] = sprintf(
284 /* translators: ... */
285 __( '<b>%s</b> – address out of range', 'wpdef' ),
286 $ip
287 );
288 }
289 } else {
290 $errors[] = sprintf(
291 /* translators: ... */
292 __( '<b>%s</b> – invalid format', 'wpdef' ),
293 $ip
294 );
295 }
296 }
297 // @since 2.6.3
298 return apply_filters( 'wd_display_ip_validations', $errors );
299 }
300
301 /**
302 * Check if there are any cloudflare headers in the request.
303 *
304 * @return bool
305 */
306 private function is_cloudflare_request() {
307 if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
308 return true;
309 }
310 if ( isset( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) {
311 return true;
312 }
313 if ( isset( $_SERVER['HTTP_CF_RAY'] ) ) {
314 return true;
315 }
316 if ( isset( $_SERVER['HTTP_CF_VISITOR'] ) ) {
317 return true;
318 }
319
320 return false;
321 }
322
323 /**
324 * We fetch the ip range here.
325 * https://www.cloudflare.com/ips/
326 *
327 * @since 2.5.0 Update the ip range.
328 *
329 * @return array
330 */
331 private function cloudflare_ip_ranges() {
332 return array(
333 array(
334 '173.245.48.0/20',
335 '103.21.244.0/22',
336 '103.22.200.0/22',
337 '103.31.4.0/22',
338 '141.101.64.0/18',
339 '108.162.192.0/18',
340 '190.93.240.0/20',
341 '188.114.96.0/20',
342 '197.234.240.0/22',
343 '198.41.128.0/17',
344 '162.158.0.0/15',
345 '172.64.0.0/13',
346 '131.0.72.0/22',
347 '104.16.0.0/13',
348 '104.24.0.0/14',
349 ),
350 array(
351 '2400:cb00::/32',
352 '2606:4700::/32',
353 '2803:f800::/32',
354 '2405:b500::/32',
355 '2405:8100::/32',
356 '2a06:98c0::/29',
357 '2c0f:f248::/32',
358 ),
359 );
360 }
361
362 /**
363 * Check if the request is from cloudflare. If it is, we get the IP.
364 *
365 * @return mixed
366 */
367 private function cloudflare_ip() {
368 if ( $this->is_cloudflare_request() ) {
369 // This looks like it come from cloudflare, so this should contain the actual IP,
370 // and REMOTE_ADDR contains cloudflare IP.
371 [$cloudflare_ipv4_range, $cloudflare_ipv6_range] = $this->cloudflare_ip_ranges();
372 $ip_helper = new IP_Helper();
373 $remote_addr = isset( $_SERVER['REMOTE_ADDR'] ) ?
374 filter_var( $_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP ) :
375 false;
376 $cf_con_ip = isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ?
377 filter_var( $_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP ) :
378 false;
379
380 if ( $remote_addr === $cf_con_ip ) {
381 return $cf_con_ip;
382 } else if ( false === $remote_addr || false === $cf_con_ip ) {
383 return false;
384 }
385
386 $is_cloudflare_proxy_ip = false;
387 if ( $this->is_v4( $remote_addr ) ) {
388 foreach ( $cloudflare_ipv4_range as $cf_ip ) {
389 if ( $ip_helper->ipv4_in_range( $remote_addr, $cf_ip ) ) {
390 $is_cloudflare_proxy_ip = true;
391 break;
392 }
393 }
394 } elseif ( $this->is_v6( $remote_addr ) ) {
395 foreach ( $cloudflare_ipv6_range as $cf_ip ) {
396 if ( $ip_helper->ipv6_in_range( $remote_addr, $cf_ip ) ) {
397 $is_cloudflare_proxy_ip = true;
398 break;
399 }
400 }
401 }
402
403 if ( true === $is_cloudflare_proxy_ip ) {
404 return $cf_con_ip;
405 }
406 }
407
408 return false;
409 }
410
411 /**
412 * Validate IP.
413 * @param $ip
414 *
415 * @return bool
416 */
417 public function check_validate_ip( $ip ) {
418 // Validate the localhost IP address.
419 if ( in_array( $ip, array( '127.0.0.1', '::1' ), true ) ) {
420 return true;
421 }
422
423 $filter_flags = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
424 // @since 2.4.7
425 if ( apply_filters( 'wp_defender_filtered_internal_ip', false ) ) {
426 // Todo: improve display of IP log when filtering reserved or private IPv4/IPv6 ranges.
427 $filter_flags = $filter_flags | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
428 }
429
430 if ( false === filter_var( $ip, FILTER_VALIDATE_IP, $filter_flags ) ) {
431 return false;
432 }
433
434 return true;
435 }
436
437 /**
438 * Get user IP.
439 *
440 * @return string
441 */
442 public function get_user_ip() {
443 // Check if it's any cloudflare IP.
444 $cf_ip = $this->cloudflare_ip();
445 if ( ! empty( $cf_ip ) && filter_var( $cf_ip, FILTER_VALIDATE_IP ) ) {
446 return apply_filters( 'defender_user_ip', $cf_ip );
447 }
448
449 $headers = array(
450 'HTTP_CLIENT_IP',
451 'HTTP_X_REAL_IP',
452 'HTTP_X_FORWARDED_FOR',
453 'HTTP_X_FORWARDED',
454 'HTTP_X_CLUSTER_CLIENT_IP',
455 'HTTP_FORWARDED_FOR',
456 'HTTP_FORWARDED',
457 'REMOTE_ADDR',
458 );
459 $ip = '';
460 foreach ( $headers as $key ) {
461 if ( array_key_exists( $key, $_SERVER ) && ! empty( $_SERVER[ $key ] ) ) {
462 $ip_array = explode( ',', $_SERVER[ $key ] );
463 $tmp_ip = array_shift( $ip_array );
464 $tmp_ip = trim( $tmp_ip );
465 if ( $this->check_validate_ip( $tmp_ip ) ) {
466 $ip = $tmp_ip;
467 break;
468 }
469 }
470 }
471
472 return apply_filters( 'defender_user_ip', $ip );
473 }
474 }
475