WordPress.org

Make WordPress Core

Changeset 42016


Ignore:
Timestamp:
10/25/2017 12:06:06 AM (2 years ago)
Author:
iandunn
Message:

Dashboard: Strip ports from IPs to avoid PHP warnings.

Fixes #41083.
Props pento, iandunn, EatonZ, birgire, dd32.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/class-wp-community-events.php

    r41605 r42016  
    234234     */
    235235    public static function get_unsafe_client_ip() {
    236         $client_ip = false;
     236        $client_ip = $netmask = false;
     237        $ip_prefix = '';
    237238
    238239        // In order of preference, with the best ones for this purpose first.
     
    261262        }
    262263
    263         // These functions are not available on Windows until PHP 5.3.
    264         if ( function_exists( 'inet_pton' ) && function_exists( 'inet_ntop' ) ) {
    265             if ( 4 === strlen( inet_pton( $client_ip ) ) ) {
    266                 $netmask = '255.255.255.0'; // ipv4.
    267             } else {
    268                 $netmask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000'; // ipv6.
    269             }
    270 
    271             $client_ip = inet_ntop( inet_pton( $client_ip ) & inet_pton( $netmask ) );
    272         }
    273 
    274         return $client_ip;
     264        if ( ! $client_ip ) {
     265            return false;
     266        }
     267
     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            $ip_start = 1;
     283            $ip_end   = (int) strpos( $client_ip, ']' ) - 1;
     284            $netmask  = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000';
     285
     286            // Strip the port (and [] from IPv6 addresses), if they exist.
     287            if ( $ip_end > 0 ) {
     288                $client_ip = substr( $client_ip, $ip_start, $ip_end );
     289            }
     290
     291            // Partially anonymize the IP by reducing it to the corresponding network ID.
     292            if ( function_exists( 'inet_pton' ) && function_exists( 'inet_ntop' ) ) {
     293                $client_ip = inet_ntop( inet_pton( $client_ip ) & inet_pton( $netmask ) );
     294            }
     295        } elseif ( $is_ipv4 ) {
     296            // Strip any port and partially anonymize the IP.
     297            $last_octet_position = strrpos( $client_ip, '.' );
     298            $client_ip           = substr( $client_ip, 0, $last_octet_position ) . '.0';
     299        } else {
     300            return false;
     301        }
     302
     303        // Restore the IPv6 prefix to compatibility mode addresses.
     304        return $ip_prefix . $client_ip;
    275305    }
    276306
  • trunk/tests/phpunit/tests/admin/includesCommunityEvents.php

    r40607 r42016  
    256256        );
    257257    }
     258
     259    /**
     260     * Test that get_unsafe_client_ip() properly anonymizes all possible address formats
     261     *
     262     * @dataProvider data_get_unsafe_client_ip_anonymization
     263     *
     264     * @ticket 41083
     265     */
     266    public function test_get_unsafe_client_ip_anonymization( $raw_ip, $expected_result ) {
     267        $_SERVER['REMOTE_ADDR'] = $raw_ip;
     268        $actual_result          = WP_Community_Events::get_unsafe_client_ip();
     269
     270        $this->assertEquals( $expected_result, $actual_result );
     271    }
     272
     273    public function data_get_unsafe_client_ip_anonymization() {
     274        return array(
     275            // Invalid IP.
     276            array(
     277                '',    // Raw IP address
     278                false, // Expected result
     279            ),
     280            // Invalid IP. Sometimes proxies add things like this, or other arbitrary strings.
     281            array(
     282                'unknown',
     283                false,
     284            ),
     285            // IPv4, no port
     286            array(
     287                '10.20.30.45',
     288                '10.20.30.0',
     289            ),
     290            // IPv4, port
     291            array(
     292                '10.20.30.45:20000',
     293                '10.20.30.0',
     294            ),
     295            // IPv6, no port
     296            array(
     297                '2a03:2880:2110:df07:face:b00c::1',
     298                '2a03:2880:2110:df07::',
     299            ),
     300            // IPv6, port
     301            array(
     302                '[2a03:2880:2110:df07:face:b00c::1]:20000',
     303                '2a03:2880:2110:df07::',
     304            ),
     305            // IPv6, no port, reducible representation
     306            array(
     307                '0000:0000:0000:0000:0000:0000:0000:0001',
     308                '::',
     309            ),
     310            // IPv6, no port, partially reducible representation
     311            array(
     312                '1000:0000:0000:0000:0000:0000:0000:0001',
     313                '1000::',
     314            ),
     315            // IPv6, port, reducible representation
     316            array(
     317                '[0000:0000:0000:0000:0000:0000:0000:0001]:1234',
     318                '::',
     319            ),
     320            // IPv6, port, partially reducible representation
     321            array(
     322                '[1000:0000:0000:0000:0000:0000:0000:0001]:5678',
     323                '1000::',
     324            ),
     325            // IPv6, no port, reduced representation
     326            array(
     327                '::',
     328                '::',
     329            ),
     330            // IPv6, no port, reduced representation
     331            array(
     332                '::1',
     333                '::',
     334            ),
     335            // IPv6, port, reduced representation
     336            array(
     337                '[::]:20000',
     338                '::',
     339            ),
     340            // IPv6, address brackets without port delimiter and number, reduced representation
     341            array(
     342                '[::1]',
     343                '::',
     344            ),
     345            // IPv6, no port, compatibility mode
     346            array(
     347                '::ffff:10.15.20.25',
     348                '::ffff:10.15.20.0',
     349            ),
     350            // IPv6, port, compatibility mode
     351            array(
     352                '[::ffff:10.15.20.25]:30000',
     353                '::ffff:10.15.20.0',
     354            ),
     355            // IPv6, no port, compatibility mode shorthand
     356            array(
     357                '::127.0.0.1',
     358                '::ffff:127.0.0.0',
     359            ),
     360            // IPv6, port, compatibility mode shorthand
     361            array(
     362                '[::127.0.0.1]:30000',
     363                '::ffff:127.0.0.0',
     364            ),
     365        );
     366    }
    258367}
Note: See TracChangeset for help on using the changeset viewer.