Changeset 42971
- Timestamp:
- 04/12/2018 09:19:24 PM (6 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/includes/class-wp-community-events.php
r42968 r42971 235 235 public static function get_unsafe_client_ip() { 236 236 $client_ip = false; 237 $ip_prefix = '';238 237 239 238 // In order of preference, with the best ones for this purpose first. … … 266 265 } 267 266 268 // Detect what kind of IP address this is. 269 $is_ipv6 = substr_count( $client_ip, ':' ) > 1; 270 $is_ipv4 = ( 3 === substr_count( $client_ip, '.' ) ); 271 272 if ( $is_ipv6 && $is_ipv4 ) { 273 // IPv6 compatibility mode, temporarily strip the IPv6 part, and treat it like IPv4. 274 $ip_prefix = '::ffff:'; 275 $client_ip = preg_replace( '/^\[?[0-9a-f:]*:/i', '', $client_ip ); 276 $client_ip = str_replace( ']', '', $client_ip ); 277 $is_ipv6 = false; 278 } 279 280 if ( $is_ipv6 ) { 281 // IPv6 addresses will always be enclosed in [] if there's a port. 282 $left_bracket = strpos( $client_ip, '[' ); 283 $right_bracket = strpos( $client_ip, ']' ); 284 $percent = strpos( $client_ip, '%' ); 285 $netmask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000'; 286 287 // Strip the port (and [] from IPv6 addresses), if they exist. 288 if ( false !== $left_bracket && false !== $right_bracket ) { 289 $client_ip = substr( $client_ip, $left_bracket + 1, $right_bracket - $left_bracket - 1 ); 290 } elseif ( false !== $left_bracket || false !== $right_bracket ) { 291 // The IP has one bracket, but not both, so it's malformed. 292 return false; 293 } 294 295 // Strip the reachability scope. 296 if ( false !== $percent ) { 297 $client_ip = substr( $client_ip, 0, $percent ); 298 } 299 300 // No invalid characters should be left. 301 if ( preg_match( '/[^0-9a-f:]/i', $client_ip ) ) { 302 return false; 303 } 304 305 // Partially anonymize the IP by reducing it to the corresponding network ID. 306 if ( function_exists( 'inet_pton' ) && function_exists( 'inet_ntop' ) ) { 307 $client_ip = inet_ntop( inet_pton( $client_ip ) & inet_pton( $netmask ) ); 308 } 309 } elseif ( $is_ipv4 ) { 310 // Strip any port and partially anonymize the IP. 311 $last_octet_position = strrpos( $client_ip, '.' ); 312 $client_ip = substr( $client_ip, 0, $last_octet_position ) . '.0'; 313 } else { 267 $anon_ip = wp_privacy_anonymize_ip( $client_ip, true ); 268 269 if ( '0.0.0.0' === $anon_ip || '::' === $anon_ip ) { 314 270 return false; 315 271 } 316 272 317 // Restore the IPv6 prefix to compatibility mode addresses. 318 return $ip_prefix . $client_ip; 273 return $anon_ip; 319 274 } 320 275 -
trunk/src/wp-includes/functions.php
r42876 r42971 6128 6128 ); 6129 6129 } 6130 6131 /** 6132 * Return an anonymized IPv4 or IPv6 address. 6133 * 6134 * @since 5.0.0 Abstracted from `WP_Community_Events::get_unsafe_client_ip()`. 6135 * 6136 * @param string $ip_addr The IPv4 or IPv6 address to be anonymized. 6137 * @param bool $ipv6_fallback Optional. Whether to return the original IPv6 address if the needed functions 6138 * to anonymize it are not present. Default false, return `::` (unspecified address). 6139 * @return string The anonymized IP address. 6140 */ 6141 function wp_privacy_anonymize_ip( $ip_addr, $ipv6_fallback = false ) { 6142 // Detect what kind of IP address this is. 6143 $ip_prefix = ''; 6144 $is_ipv6 = substr_count( $ip_addr, ':' ) > 1; 6145 $is_ipv4 = ( 3 === substr_count( $ip_addr, '.' ) ); 6146 6147 if ( $is_ipv6 && $is_ipv4 ) { 6148 // IPv6 compatibility mode, temporarily strip the IPv6 part, and treat it like IPv4. 6149 $ip_prefix = '::ffff:'; 6150 $ip_addr = preg_replace( '/^\[?[0-9a-f:]*:/i', '', $ip_addr ); 6151 $ip_addr = str_replace( ']', '', $ip_addr ); 6152 $is_ipv6 = false; 6153 } 6154 6155 if ( $is_ipv6 ) { 6156 // IPv6 addresses will always be enclosed in [] if there's a port. 6157 $left_bracket = strpos( $ip_addr, '[' ); 6158 $right_bracket = strpos( $ip_addr, ']' ); 6159 $percent = strpos( $ip_addr, '%' ); 6160 $netmask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000'; 6161 6162 // Strip the port (and [] from IPv6 addresses), if they exist. 6163 if ( false !== $left_bracket && false !== $right_bracket ) { 6164 $ip_addr = substr( $ip_addr, $left_bracket + 1, $right_bracket - $left_bracket - 1 ); 6165 } elseif ( false !== $left_bracket || false !== $right_bracket ) { 6166 // The IP has one bracket, but not both, so it's malformed. 6167 return '::'; 6168 } 6169 6170 // Strip the reachability scope. 6171 if ( false !== $percent ) { 6172 $ip_addr = substr( $ip_addr, 0, $percent ); 6173 } 6174 6175 // No invalid characters should be left. 6176 if ( preg_match( '/[^0-9a-f:]/i', $ip_addr ) ) { 6177 return '::'; 6178 } 6179 6180 // Partially anonymize the IP by reducing it to the corresponding network ID. 6181 if ( function_exists( 'inet_pton' ) && function_exists( 'inet_ntop' ) ) { 6182 $ip_addr = inet_ntop( inet_pton( $ip_addr ) & inet_pton( $netmask ) ); 6183 if ( false === $ip_addr) { 6184 return '::'; 6185 } 6186 } elseif ( ! $ipv6_fallback ) { 6187 return '::'; 6188 } 6189 } elseif ( $is_ipv4 ) { 6190 // Strip any port and partially anonymize the IP. 6191 $last_octet_position = strrpos( $ip_addr, '.' ); 6192 $ip_addr = substr( $ip_addr, 0, $last_octet_position ) . '.0'; 6193 } else { 6194 return '0.0.0.0'; 6195 } 6196 6197 // Restore the IPv6 prefix to compatibility mode addresses. 6198 return $ip_prefix . $ip_addr; 6199 } 6200 6201 /** 6202 * Return uniform "anonymous" data by type. 6203 * 6204 * @since 5.0.0 6205 * 6206 * @param string $type The type of data to be anonymized. 6207 * @param string $data Optional The data to be anonymized. 6208 * @return string The anonymous data for the requested type. 6209 */ 6210 function wp_privacy_anonymize_data( $type, $data = '' ) { 6211 6212 switch ( $type ) { 6213 case 'email': 6214 $anonymous = 'deleted@site.invalid'; 6215 break; 6216 case 'url': 6217 $anonymous = 'https://site.invalid'; 6218 break; 6219 case 'ip': 6220 $anonymous = wp_privacy_anonymize_ip( $data ); 6221 break; 6222 case 'date': 6223 $anonymous = '0000-00-00 00:00:00'; 6224 break; 6225 case 'text': 6226 /* translators: deleted text */ 6227 $anonymous = __( '[deleted]' ); 6228 break; 6229 case 'longtext': 6230 /* translators: deleted long text */ 6231 $anonymous = __( 'This content was deleted by the author.' ); 6232 break; 6233 default: 6234 $anonymous = ''; 6235 } 6236 6237 /** 6238 * Filters the anonymous data for each type. 6239 * 6240 * @since 5.0.0 6241 * 6242 * @param string $anonymous Anonymized data. 6243 * @param string $type Type of the data. 6244 * @param string $data Original data. 6245 */ 6246 return apply_filters( 'wp_privacy_anonymize_data', $anonymous, $type, $data ); 6247 } -
trunk/tests/phpunit/tests/admin/includesCommunityEvents.php
r42968 r42971 480 480 * Test that get_unsafe_client_ip() properly anonymizes all possible address formats 481 481 * 482 * @dataProvider data_get_unsafe_client_ip _anonymization482 * @dataProvider data_get_unsafe_client_ip 483 483 * 484 484 * @ticket 41083 485 485 */ 486 public function test_get_unsafe_client_ip_anonymization( $raw_ip, $expected_result ) { 487 $_SERVER['REMOTE_ADDR'] = $raw_ip; 488 $actual_result = WP_Community_Events::get_unsafe_client_ip(); 486 public function test_get_unsafe_client_ip( $raw_ip, $expected_result ) { 487 $_SERVER['REMOTE_ADDR'] = 'this should not be used'; 488 $_SERVER['HTTP_CLIENT_IP'] = $raw_ip; 489 $actual_result = WP_Community_Events::get_unsafe_client_ip(); 489 490 490 491 $this->assertEquals( $expected_result, $actual_result ); 491 492 } 492 493 493 public function data_get_unsafe_client_ip_anonymization() { 494 return array( 495 // Invalid IP. 494 /** 495 * Provide test cases for `test_get_unsafe_client_ip()`. 496 * 497 * @return array 498 */ 499 public function data_get_unsafe_client_ip() { 500 return array( 501 // Handle '::' returned from `wp_privacy_anonymize_ip()`. 496 502 array( 497 '', // Raw IP address 498 false, // Expected result 499 ), 500 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 503 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001', 504 false, 505 ), 506 507 // Handle '0.0.0.0' returned from `wp_privacy_anonymize_ip()`. 501 508 array( 502 509 'unknown', 503 510 false, 504 511 ), 505 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 512 513 // Valid IPv4. 506 514 array( 507 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001', 508 false, 509 ), 510 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 511 array( 512 'or=\"1000:0000:0000:0000:0000:0000:0000:0001', 513 false, 514 ), 515 // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings. 516 array( 517 '1000:0000:0000:0000:0000:0000:0000:0001or=\"', 518 false, 519 ), 520 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 521 array( 522 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]:400', 523 '1000::', 524 ), 525 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 526 array( 527 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]', 528 '1000::', 529 ), 530 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 531 array( 532 'or=\"[1000:0000:0000:0000:0000:0000:0000:0001]400', 533 '1000::', 534 ), 535 // Malformed string with valid IP substring. Sometimes proxies add things like this, or other arbitrary strings. 536 array( 537 '[1000:0000:0000:0000:0000:0000:0000:0001]:235\"or=', 538 '1000::', 539 ), 540 // IPv4, no port 541 array( 542 '10.20.30.45', 543 '10.20.30.0', 544 ), 545 // IPv4, port 546 array( 547 '10.20.30.45:20000', 548 '10.20.30.0', 549 ), 550 // IPv6, no port 515 '198.143.164.252', 516 '198.143.164.0', 517 ), 518 519 // Valid IPv6. 551 520 array( 552 521 '2a03:2880:2110:df07:face:b00c::1', 553 522 '2a03:2880:2110:df07::', 554 523 ), 555 // IPv6, port556 array(557 '[2a03:2880:2110:df07:face:b00c::1]:20000',558 '2a03:2880:2110:df07::',559 ),560 // IPv6, no port, reducible representation561 array(562 '0000:0000:0000:0000:0000:0000:0000:0001',563 '::',564 ),565 // IPv6, no port, partially reducible representation566 array(567 '1000:0000:0000:0000:0000:0000:0000:0001',568 '1000::',569 ),570 // IPv6, port, reducible representation571 array(572 '[0000:0000:0000:0000:0000:0000:0000:0001]:1234',573 '::',574 ),575 // IPv6, port, partially reducible representation576 array(577 '[1000:0000:0000:0000:0000:0000:0000:0001]:5678',578 '1000::',579 ),580 // IPv6, no port, reduced representation581 array(582 '::',583 '::',584 ),585 // IPv6, no port, reduced representation586 array(587 '::1',588 '::',589 ),590 // IPv6, port, reduced representation591 array(592 '[::]:20000',593 '::',594 ),595 // IPv6, address brackets without port delimiter and number, reduced representation596 array(597 '[::1]',598 '::',599 ),600 // IPv6, no port, compatibility mode601 array(602 '::ffff:10.15.20.25',603 '::ffff:10.15.20.0',604 ),605 // IPv6, port, compatibility mode606 array(607 '[::FFFF:10.15.20.25]:30000',608 '::ffff:10.15.20.0',609 ),610 // IPv6, no port, compatibility mode shorthand611 array(612 '::127.0.0.1',613 '::ffff:127.0.0.0',614 ),615 // IPv6, port, compatibility mode shorthand616 array(617 '[::127.0.0.1]:30000',618 '::ffff:127.0.0.0',619 ),620 // IPv6 with reachability scope621 array(622 'fe80::b059:65f4:e877:c40%16',623 'fe80::',624 ),625 // IPv6 with reachability scope626 array(627 'FE80::B059:65F4:E877:C40%eth0',628 'fe80::',629 ),630 524 ); 631 525 }
Note: See TracChangeset
for help on using the changeset viewer.