Validating non-private IP addresses with PHP

2020-06-12 05:15发布

问题:

I'm trying to check whether or not an IP address is an internal-only (i.e. private) IP, but I'm getting a curious result:

filter_var('173.194.66.94', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); // returns 173.194.66.94
filter_var('192.168.0.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); // returns false
filter_var('127.0.0.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); // returns 127.0.0.1?

Surely 127.0.0.1 counts as a private IP? I found this bug report from 2010 which reports this as an issue, but it's marked as fixed. Is this a regression, or am I misunderstanding what this filter does? I'm using PHP 5.4.6.

回答1:

I guess thats because 127.0.0.1 is not realy a private IP range, but a loopback IP range, as explained here

Normally, when a TCP/IP application wants to send information, that information travels down the protocol layers to IP where it is encapsulated in an IP datagram. That datagram then passes down to the data link layer of the device's physical network for transmission to the next hop, on the way to the IP destination.

However, one special range of addresses is set aside for loopback functionality. This is the range 127.0.0.0 to 127.255.255.255. IP datagrams sent by a host to a 127.x.x.x loopback address are not passed down to the data link layer for transmission. Instead, they “loop back” to the source device at the IP level. In essence, this represents a “short-circuiting” of the normal protocol stack; data is sent by a device's layer three IP implementation and then immediately received by it.

The purpose of the loopback range is testing of the TCP/IP protocol implementation on a host. Since the lower layers are short-circuited, sending to a loopback address allows the higher layers (IP and above) to be effectively tested without the chance of problems at the lower layers manifesting themselves. 127.0.0.1 is the address most commonly used for testing purposes.

The manual for Filter flag has a comment on this specific issue.

<?php
function FILTER_FLAG_NO_LOOPBACK_RANGE($value) {
    // Fails validation for the following loopback IPv4 range: 127.0.0.0/8
    // This flag does not apply to IPv6 addresses
    return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? $value :
        (((ip2long($value) & 0xff000000) == 0x7f000000) ? FALSE : $value);
}

$var = filter_var('127.0.0.1', FILTER_CALLBACK, array('options' => 'FILTER_FLAG_NO_LOOPBACK_RANGE'));
// Returns FALSE

$var = filter_var('74.125.19.103', FILTER_CALLBACK, array('options' => 'FILTER_FLAG_NO_LOOPBACK_RANGE'));
// Returns '74.125.19.103'

// To filter Private IP ranges and Loopback ranges
$var = filter_var('127.0.0.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)  && filter_var('127.0.0.1', FILTER_CALLBACK, array('options' => 'FILTER_FLAG_NO_LOOPBACK_RANGE'));
// Returns FALSE
?>


回答2:

It's now blocked by FILTER_FLAG_NO_RES_RANGE.

See https://bugs.php.net/bug.php?id=53150