WordPress.org

Make WordPress Core

Ticket #41083: 41083.2.diff

File 41083.2.diff, 5.0 KB (added by iandunn, 2 years ago)

Alternative to filter_var

  • src/wp-admin/includes/class-wp-community-events.php

    diff --git src/wp-admin/includes/class-wp-community-events.php src/wp-admin/includes/class-wp-community-events.php
    index 053d92db1a..edea9492cb 100644
    class WP_Community_Events { 
    224224         *                      or false on failure.
    225225         */
    226226        public static function get_unsafe_client_ip() {
    227                 $client_ip = false;
     227                $client_ip = $netmask = false;
    228228
    229229                // In order of preference, with the best ones for this purpose first.
    230230                $address_headers = array(
    class WP_Community_Events { 
    237237                        'REMOTE_ADDR',
    238238                );
    239239
     240                // Determine the best address to use
    240241                foreach ( $address_headers as $header ) {
    241242                        if ( array_key_exists( $header, $_SERVER ) ) {
    242243                                /*
    class WP_Community_Events { 
    251252                        }
    252253                }
    253254
    254                 // These functions are not available on Windows until PHP 5.3.
    255                 if ( function_exists( 'inet_pton' ) && function_exists( 'inet_ntop' ) ) {
    256                         if ( 4 === strlen( inet_pton( $client_ip ) ) ) {
    257                                 $netmask = '255.255.255.0'; // ipv4.
    258                         } else {
    259                                 $netmask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000'; // ipv6.
    260                         }
     255                $address_format = self::detect_ip_address_format( (string) $client_ip );
     256
     257                if ( ! $address_format ) {
     258                        $client_ip = false;
     259                }
    261260
     261                if ( $client_ip ) {
     262                        $client_ip = self::strip_ip_address_port( $client_ip, $address_format );
     263                        $netmask   = 4 === $address_format ? '255.255.255.0' : 'ffff:ffff:ffff:ffff:0000:0000:0000:0000';
     264                }
     265
     266                // Partially anonymize the IP by reducing it to the corresponding network ID
     267                // These functions are not available on Windows until PHP 5.3.
     268                if ( $client_ip && $netmask && function_exists( 'inet_pton' ) && function_exists( 'inet_ntop' ) ) {
    262269                        $client_ip = inet_ntop( inet_pton( $client_ip ) & inet_pton( $netmask ) );
    263270                }
    264271
    class WP_Community_Events { 
    266273        }
    267274
    268275        /**
     276         * Detect the format of an IP address.
     277         *
     278         * `filter_var()` is avoided because it can be disabled in PHP builds, and
     279         * `WP_Http::is_ip_address()` is avoided because it will return false if the address
     280         * contains a port number.
     281         *
     282         * A valid IPv4 address must have exactly 3 periods and at most 1 colon.
     283         * A valid IPv6 address cannot have periods, and must have at least two colons (because
     284         * addresses like `0:0:0:0:0:0:0:0` can be reduced to `::`).
     285         *
     286         * @param string $address
     287         *
     288         * @return false|int todo document false, 4, 6
     289         */
     290        protected static function detect_ip_address_format( $address ) {
     291                $format = false;
     292
     293                if ( 3 === substr_count( $address, '.' ) ) {
     294                        $format = 4;
     295                } elseif ( substr_count( $address, ':' ) >= 2 ) {
     296                        $format = 6;
     297                }
     298
     299                return $format;
     300        }
     301
     302        // todo document
     303        protected static function strip_ip_address_port( $ip_address, $address_format ) {
     304                if ( 4 === $address_format ) {
     305                        $port_delimiter = strpos( $ip_address, ':' );
     306
     307                        if ( $port_delimiter ) {
     308                                $ip_address = substr( $ip_address, 0, $port_delimiter );
     309                        }
     310                } elseif ( 6 === $address_format ) {
     311                        $port_delimiter = strpos( $ip_address, ']' );
     312
     313                        if ( $port_delimiter ) {
     314                                $ip_address = substr( $ip_address, 1, $port_delimiter - 1 );
     315                                // todo verify it always has the brackets when there's a port
     316                        }
     317                }
     318
     319                return $ip_address;
     320        }
     321
     322
     323        /**
    269324         * Test if two pairs of latitude/longitude coordinates match each other.
    270325         *
    271326         * @since 4.8.0
  • tests/phpunit/tests/admin/includesCommunityEvents.php

    diff --git tests/phpunit/tests/admin/includesCommunityEvents.php tests/phpunit/tests/admin/includesCommunityEvents.php
    index ed5cc2caee..32864f5a0f 100644
    class Test_WP_Community_Events extends WP_UnitTestCase { 
    255255                        'filename' => '',
    256256                );
    257257        }
     258
     259        /**
     260         * Test that get_unsafe_client_ip() properly anonymizes all possible address formats
     261         *
     262         * @ticket 41083
     263         * @since 4.8.1
     264         */
     265        public function test_get_unsafe_client_ip_anonymization() {
     266                $test_cases = array(
     267                        false                                      => false,                    // Invalid IP
     268                        'unknown'                                  => false,                    // Invalid IP
     269                        '10.20.30.45'                              => '10.20.30.0',             // IPv4, no port
     270                        '10.20.30.45:20000'                        => '10.20.30.0',             // IPv4, port
     271                        '2a03:2880:2110:df07:face:b00c::1'         => '2a03:2880:2110:df07::',  // IPv6, no port
     272                        '[2a03:2880:2110:df07:face:b00c::1]:20000' => '2a03:2880:2110:df07::',  // IPv6, port
     273                        '::'                                       => '::',                     // IPv6, no port, reduced representation
     274                        '[::]:20000'                               => '::',                     // IPv6, port, reduced representation
     275                );
     276
     277                foreach ( $test_cases as $raw_ip => $expected_result ) {
     278                        $_SERVER['REMOTE_ADDR'] = $raw_ip;
     279                        $actual_result          = WP_Community_Events::get_unsafe_client_ip();
     280
     281                        $this->assertEquals( $expected_result, $actual_result );
     282                }
     283        }
    258284}